Skip to content

Commit 826a51a

Browse files
committed
encoding/ewkb: add PrefixSRIDScanner
1 parent e0c3d2e commit 826a51a

File tree

2 files changed

+89
-5
lines changed

2 files changed

+89
-5
lines changed

encoding/ewkb/scanner.go

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package ewkb
33
import (
44
"database/sql"
55
"database/sql/driver"
6+
"encoding/binary"
67

78
"github.com/paulmach/orb"
89
"github.com/paulmach/orb/encoding/internal/wkbcommon"
@@ -26,10 +27,11 @@ var (
2627
// // NULL value
2728
// }
2829
type GeometryScanner struct {
29-
g interface{}
30-
SRID int
31-
Geometry orb.Geometry
32-
Valid bool // Valid is true if the geometry is not NULL
30+
sridInPrefix bool
31+
g interface{}
32+
SRID int
33+
Geometry orb.Geometry
34+
Valid bool // Valid is true if the geometry is not NULL
3335
}
3436

3537
// Scanner will return a GeometryScanner that can scan sql query results.
@@ -58,18 +60,63 @@ func Scanner(g interface{}) *GeometryScanner {
5860
return &GeometryScanner{g: g}
5961
}
6062

63+
// ScannerPrefixSRID will scan ewkb data were the SRID is in the first 4 bytes of the data.
64+
// Databases like mysql/mariadb use this as their raw format. This method should only be used when
65+
// working with such a database.
66+
//
67+
// var p orb.Point
68+
// err := db.QueryRow("SELECT latlon FROM foo WHERE id=?", id).Scan(wkb.PrefixSRIDScanner(&p))
69+
//
70+
// However, it is recommended to covert to wkb explicitly using something like:
71+
//
72+
// var srid int
73+
// var p orb.Point
74+
// err := db.QueryRow("SELECT ST_SRID(latlon), ST_AsBinary(latlon) FROM foo WHERE id=?", id).
75+
// Scan(&srid, wkb.Scanner(&p))
76+
func ScannerPrefixSRID(g interface{}) *GeometryScanner {
77+
return &GeometryScanner{sridInPrefix: true, g: g}
78+
}
79+
6180
// Scan will scan the input []byte data into a geometry.
6281
// This could be into the orb geometry type pointer or, if nil,
6382
// the scanner.Geometry attribute.
6483
func (s *GeometryScanner) Scan(d interface{}) error {
6584
s.Geometry = nil
6685
s.Valid = false
6786

68-
g, srid, valid, err := wkbcommon.Scan(s.g, d)
87+
var (
88+
srid int
89+
data interface{}
90+
)
91+
92+
data = d
93+
if s.sridInPrefix {
94+
raw, ok := d.([]byte)
95+
if !ok {
96+
return ErrUnsupportedDataType
97+
}
98+
99+
if raw == nil {
100+
return nil
101+
}
102+
103+
if len(raw) < 5 {
104+
return ErrNotEWKB
105+
}
106+
107+
srid = int(binary.LittleEndian.Uint32(raw))
108+
data = raw[4:]
109+
}
110+
111+
g, embeddedSRID, valid, err := wkbcommon.Scan(s.g, data)
69112
if err != nil {
70113
return mapCommonError(err)
71114
}
72115

116+
if embeddedSRID != 0 {
117+
srid = embeddedSRID
118+
}
119+
73120
s.Geometry = g
74121
s.SRID = srid
75122
s.Valid = valid

encoding/ewkb/scanner_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,43 @@ func TestScanPoint_errors(t *testing.T) {
374374
}
375375
}
376376

377+
func TestPrefixScannerPrefixSRID(t *testing.T) {
378+
cases := []struct {
379+
name string
380+
data []byte
381+
srid int
382+
expected orb.Point
383+
}{
384+
{
385+
name: "point",
386+
data: append([]byte{230, 16, 0, 0}, MustMarshal(orb.Point{4, 5}, 0)...),
387+
srid: 4326,
388+
expected: orb.Point{4, 5},
389+
},
390+
}
391+
392+
for _, tc := range cases {
393+
t.Run(tc.name, func(t *testing.T) {
394+
var p orb.Point
395+
s := ScannerPrefixSRID(&p)
396+
err := s.Scan(tc.data)
397+
if err != nil {
398+
t.Fatalf("scan error: %v", err)
399+
}
400+
401+
if !p.Equal(tc.expected) {
402+
t.Errorf("unequal data")
403+
t.Log(p)
404+
t.Log(tc.expected)
405+
}
406+
407+
if s.SRID != tc.srid {
408+
t.Errorf("incorrect SRID: %v != %v", s.SRID, tc.srid)
409+
}
410+
})
411+
}
412+
}
413+
377414
func TestScanMultiPoint(t *testing.T) {
378415
cases := []struct {
379416
name string

0 commit comments

Comments
 (0)