-
Notifications
You must be signed in to change notification settings - Fork 0
/
ShpReader.go
313 lines (287 loc) · 9.38 KB
/
ShpReader.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
// shpReader
package shpReader
//package main
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io/ioutil"
"math"
)
/*
Value Shape type Fields
0 Null shape None
1 Point X, Y
3 Polyline MBR, Number of parts, Number of points, Parts, Points
5 Polygon MBR, Number of parts, Number of points, Parts, Points
8 MultiPoint MBR, Number of points, Points
11 PointZ X, Y, Z, M
13 PolylineZ Mandatory: MBR, Number of parts, Number of points, Parts, Points, Z range, Z array Optional: M range, M array
15 PolygonZ Mandatory: MBR, Number of parts, Number of points, Parts, Points, Z range, Z array Optional: M range, M array
18 MultiPointZ Mandatory: MBR, Number of points, Points, Z range, Z array Optional: M range, M array
21 PointM X, Y, M
23 PolylineM Mandatory: MBR, Number of parts, Number of points, Parts, Points Optional: M range, M array
25 PolygonM Mandatory: MBR, Number of parts, Number of points, Parts, Points Optional: M range, M array
28 MultiPointM Mandatory: MBR, Number of points, Points Optional Fields: M range, M array
31 MultiPatch Mandatory: MBR, Number of parts, Number of points, Parts, Part types, Points, Z range, Z array
Optional: M range, M array
*/
const (
NULLSHAPE = 0
POINT = 1
POLYLINE = 3
POLYGON = 5
MULTIPOINT = 8
POINTZ = 11
POLYLINEZ = 13
POLYGONZ = 15
MULTIPOINTZ = 18
POINTM = 21
POLYLINEM = 23
POLYGONM = 25
MULTIPOINTM = 28
MULTIPATH = 31
)
type Header struct {
/*
Bytes Type Endianness Usage
00–03 int32 big File code (always hex value 0x0000270a)*/
FileCode uint32
/* 04–23 int32 big Unused; five uint32 */
_ [20]byte
/* 24–27 int32 big File length (in 16-bit words, including the header)*/
FileLength int32
/* 28–31 int32 little Version*/
Version int32
/* 32–35 int32 little Shape type (see reference below)*/
ShapeType int32
/* 36–67 double little Minimum bounding rectangle (MBR) of all shapes contained within the shapefile; four doubles in the following order: min X, min Y, max X, max Y */
MinX float64
MinY float64
MaxX float64
MaxY float64
/* 68–83 double little Range of Z; two doubles in the following order: min Z, max Z */
MinZ float64
MaxZ float64
/* 84–99 double little Range of M; two doubles in the following order: min M, max M */
MinM float64
MaxM float64
/* The file then contains any number of variable-length records. Each record is prefixed with a record-header of 8
bytes:
Bytes Type Endianness Usage
0–3 int32 big Record number (1-based)
4–7 int32 big Record length (in 16-bit words)
Following the record header is the actual record:
Bytes Type Endianness Usage
0–3 int32 little Shape type (see reference above)
4– - - Shape content
*/
}
func (h Header) String() string {
return fmt.Sprintf("filecode :%#X\nfilelength :%v\nversion :%v\nshape :%d\nminX :%2.2f\nmaxX :%2.2f\nminY :%2.2f\nmaxY :%2.2f\n",
h.FileCode, h.FileLength, h.Version, h.ShapeType,
h.MinX, h.MaxX,
h.MinY, h.MaxY)
}
func readHeader(bf *BinFileReader) (header Header, err error) {
content := bf.ReadByte(100)
buffer := bytes.NewBuffer(content)
header = Header{}
if err = binary.Read(buffer, binary.LittleEndian, &header); err != nil {
return
}
header.FileCode = swapEncodingUint32(header.FileCode)
if header.FileCode != 0X270A {
err = errors.New("Not a Shapefile")
}
return
}
func writeHeader(header *Header) (err error) {
buf := new(bytes.Buffer)
header.FileCode = swapEncodingUint32(header.FileCode)
if err = binary.Write(buf, binary.LittleEndian, header); err != nil {
return
}
// raw data -> file fmt.Println(buf)
fmt.Printf("% x\n", buf.Bytes())
return
}
func swapEncodingUint16(val uint16) uint16 { //BigEndian <->LittleEndian
return (val >> 8) | (val << 8)
}
func swapEncodingUint32(val uint32) uint32 { //BigEndian <->LittleEndian
return ((val >> 24) & 0xff) | // move byte 3 to byte 0
((val << 8) & 0xff0000) | // move byte 1 to byte 2
((val >> 8) & 0xff00) | // move byte 2 to byte 1
((val << 24) & 0xff000000) // move byte 0 to byte 3
}
type ShapeData struct {
RecordNum int32
ContentLength int32
ShapeType int32 //Big endian
Box0 float64
Box1 float64
Box2 float64
Box3 float64
NumParts int32 //Big endian
NumPoints int32 //Big endian
PartCount []int
Coordinates [][][2]float64
}
func (s ShapeData) String() string {
outStr := "Coordinates:\n X , Y\n"
for _, p := range s.Coordinates {
for _, c := range p {
outStr += fmt.Sprintf("%2.2f,%2.2f\n", c[0], c[1])
}
outStr += ",\n"
}
return fmt.Sprintf("Record number:%d Content length:%d Shape type:%d Bounding box: %2.2f, %2.2f, %2.2f, %2.2f, Num parts: %d Num Points: %d, part count : %v \n%v",
s.RecordNum, s.ContentLength, s.ShapeType, s.Box0, s.Box1, s.Box2, s.Box3, s.NumParts, s.NumPoints, s.PartCount, outStr)
}
func ReadPolygons(bf *BinFileReader) (header Header, data []ShapeData, err error) {
/*
***vvv Description of Main File Record Headers vvv***
Byte Position Field Value Type Order
Byte 0 Record Number Record Number Integer Big
Byte 4 Content Length Content Length Integer Big
***^^^ Description of Main File Record Headers ^^^***
Polygon
{
Float[4] Box // Bounding Box
Integer NumParts // Number of Parts
Integer NumPoints // Total Number of Points
Integer[NumParts] Parts // Index to First Point in Part
Point[NumPoints] Points // Points for All Parts
}
***vvv Polygon Record Contents vvv***
Position Field Value Type Number Order
Byte 0 Shape Type 5 Integer 1 Little
Byte 4 Box Box Double 4 Little
Byte 36 NumParts NumParts Integer 1 Little
Byte 40 NumPoints NumPoints Integer 1 Little
Byte 44 Parts Parts Integer NumParts Little
Byte X Points Points Point NumPoints Little
note X = 44 + 4 * numParts
***^^^ Polygon Record Contents ^^^***
*/
//shapeNo := 0
var shapesData []ShapeData
head, err := readHeader(bf)
if err != nil {
return
}
for !bf.EOF() {
shapeData := ShapeData{} //shapeNo++
shapeData.RecordNum = bf.ReadIntLittle()
shapeData.ContentLength = bf.ReadIntLittle()
shapeData.ShapeType = bf.ReadIntBig()
shapeData.Box0 = bf.ReadFloatLittle()
shapeData.Box1 = bf.ReadFloatLittle()
shapeData.Box2 = bf.ReadFloatLittle()
shapeData.Box3 = bf.ReadFloatLittle()
shapeData.NumParts = bf.ReadIntBig()
shapeData.NumPoints = bf.ReadIntBig()
shapeData.PartCount = make([]int, shapeData.NumParts)
for i := 0; i < int(shapeData.NumParts); i++ {
shapeData.PartCount[i] = int(bf.ReadIntBig())
}
count := 0
for count = 0; count < len(shapeData.PartCount)-1; count++ {
shapeData.PartCount[count] = shapeData.PartCount[count+1]
}
shapeData.PartCount[count] = int(shapeData.NumPoints)
j := 0
for i := 0; i < int(shapeData.NumParts); i++ {
var points [][2]float64
for ; j < shapeData.PartCount[i]; j++ {
x := bf.ReadFloatLittle()
y := bf.ReadFloatLittle()
points = append(points, [2]float64{x, y})
}
shapeData.Coordinates = append(shapeData.Coordinates, points)
}
shapesData = append(shapesData, shapeData)
}
return head, shapesData, err
}
type BinFileReader struct {
b []byte
pos int
length int
}
func New(filename string) (bf BinFileReader, err error) {
content, err := ioutil.ReadFile(filename)
if err != nil {
return
}
//fmt.Println(len(content))
bf.b = content
bf.pos = 0
bf.length = len(bf.b)
return
}
func (bf *BinFileReader) EOF() bool {
return bf.pos >= bf.length
}
func (bf *BinFileReader) ReadByte(nBytes int) []byte {
buf := make([]byte, nBytes)
for i := 0; i < nBytes; i++ {
buf[i] = bf.b[bf.pos]
bf.pos++
}
return buf
}
func (bf *BinFileReader) ReadIntBig() int32 {
buf := make([]byte, 4)
for i := 0; i < 4; i++ {
buf[i] = bf.b[bf.pos]
bf.pos++
}
return int32(buf[3])<<24 | int32(buf[2])<<16 | int32(buf[1])<<8 | int32(buf[0])
}
func (bf *BinFileReader) ReadIntLittle() int32 {
buf := make([]byte, 4)
for i := 0; i < 4; i++ {
buf[i] = bf.b[bf.pos]
bf.pos++
}
return int32(buf[0])<<24 | int32(buf[1])<<16 | int32(buf[2])<<8 | int32(buf[3])
}
func (bf *BinFileReader) ReadFloatBig() float64 {
buf := make([]byte, 8)
for i := 0; i < 8; i++ {
buf[i] = bf.b[bf.pos]
bf.pos++
}
bits := binary.LittleEndian.Uint64(buf)
return math.Float64frombits(bits)
}
func (bf *BinFileReader) ReadFloatLittle() float64 {
buf := make([]byte, 8)
for i := 0; i < 8; i++ {
buf[i] = bf.b[bf.pos]
bf.pos++
}
bits := binary.LittleEndian.Uint64(buf)
return math.Float64frombits(bits)
}
//func main() {
// bf, err := New("cntry02.shp")
// if err != nil {
// fmt.Println(err)
// return
// }
// head, shapes, err := ReadPolygons(&bf)
// if err != nil {
// fmt.Println(err)
// }
// if err = writeHeader(&head); err != nil {
// fmt.Println(err)
// }
// fmt.Println(head)
// for _, shape := range shapes {
// fmt.Println(shape)
// }
//}