Skip to content

Commit 2f8f920

Browse files
authored
Merge pull request #123 from paulmach/bson
geojson: marshal/unmarshal BSON
2 parents ddd8769 + ad0dea9 commit 2f8f920

File tree

8 files changed

+799
-51
lines changed

8 files changed

+799
-51
lines changed

geojson/feature.go

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55

66
"github.com/paulmach/orb"
7+
"go.mongodb.org/mongo-driver/bson"
78
)
89

910
// A Feature corresponds to GeoJSON feature object
@@ -37,19 +38,30 @@ var _ orb.Pointer = &Feature{}
3738
// It will handle the encoding of all the child geometries.
3839
// Alternately one can call json.Marshal(f) directly for the same result.
3940
func (f Feature) MarshalJSON() ([]byte, error) {
40-
jf := &jsonFeature{
41+
return marshalJSON(newFeatureDoc(&f))
42+
}
43+
44+
// MarshalBSON converts the feature object into the proper JSON.
45+
// It will handle the encoding of all the child geometries.
46+
// Alternately one can call json.Marshal(f) directly for the same result.
47+
func (f Feature) MarshalBSON() ([]byte, error) {
48+
return bson.Marshal(newFeatureDoc(&f))
49+
}
50+
51+
func newFeatureDoc(f *Feature) *featureDoc {
52+
doc := &featureDoc{
4153
ID: f.ID,
4254
Type: "Feature",
4355
Properties: f.Properties,
4456
BBox: f.BBox,
4557
Geometry: NewGeometry(f.Geometry),
4658
}
4759

48-
if len(jf.Properties) == 0 {
49-
jf.Properties = nil
60+
if len(doc.Properties) == 0 {
61+
doc.Properties = nil
5062
}
5163

52-
return marshalJSON(jf)
64+
return doc
5365
}
5466

5567
// UnmarshalFeature decodes the data into a GeoJSON feature.
@@ -67,39 +79,54 @@ func UnmarshalFeature(data []byte) (*Feature, error) {
6779
// UnmarshalJSON handles the correct unmarshalling of the data
6880
// into the orb.Geometry types.
6981
func (f *Feature) UnmarshalJSON(data []byte) error {
70-
jf := &jsonFeature{}
71-
err := unmarshalJSON(data, &jf)
82+
doc := &featureDoc{}
83+
err := unmarshalJSON(data, &doc)
84+
if err != nil {
85+
return err
86+
}
87+
88+
return featureUnmarshalFinish(doc, f)
89+
}
90+
91+
// UnmarshalBSON will unmarshal a BSON document created with bson.Marshal.
92+
func (f *Feature) UnmarshalBSON(data []byte) error {
93+
doc := &featureDoc{}
94+
err := bson.Unmarshal(data, &doc)
7295
if err != nil {
7396
return err
7497
}
7598

76-
if jf.Type != "Feature" {
77-
return fmt.Errorf("geojson: not a feature: type=%s", jf.Type)
99+
return featureUnmarshalFinish(doc, f)
100+
}
101+
102+
func featureUnmarshalFinish(doc *featureDoc, f *Feature) error {
103+
if doc.Type != "Feature" {
104+
return fmt.Errorf("geojson: not a feature: type=%s", doc.Type)
78105
}
79106

80107
var g orb.Geometry
81-
if jf.Geometry != nil {
82-
if jf.Geometry.Coordinates == nil && jf.Geometry.Geometries == nil {
108+
if doc.Geometry != nil {
109+
if doc.Geometry.Coordinates == nil && doc.Geometry.Geometries == nil {
83110
return ErrInvalidGeometry
84111
}
85-
g = jf.Geometry.Geometry()
112+
g = doc.Geometry.Geometry()
86113
}
87114

88115
*f = Feature{
89-
ID: jf.ID,
90-
Type: jf.Type,
91-
Properties: jf.Properties,
92-
BBox: jf.BBox,
116+
ID: doc.ID,
117+
Type: doc.Type,
118+
Properties: doc.Properties,
119+
BBox: doc.BBox,
93120
Geometry: g,
94121
}
95122

96123
return nil
97124
}
98125

99-
type jsonFeature struct {
100-
ID interface{} `json:"id,omitempty"`
101-
Type string `json:"type"`
102-
BBox BBox `json:"bbox,omitempty"`
103-
Geometry *Geometry `json:"geometry"`
104-
Properties Properties `json:"properties"`
126+
type featureDoc struct {
127+
ID interface{} `json:"id,omitempty" bson:"id"`
128+
Type string `json:"type" bson:"type"`
129+
BBox BBox `json:"bbox,omitempty" bson:"bbox,omitempty"`
130+
Geometry *Geometry `json:"geometry" bson:"geometry"`
131+
Properties Properties `json:"properties" bson:"properties"`
105132
}

geojson/feature_collection.go

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ package geojson
88

99
import (
1010
"fmt"
11+
12+
"go.mongodb.org/mongo-driver/bson"
1113
)
1214

1315
const featureCollection = "FeatureCollection"
@@ -44,6 +46,21 @@ func (fc *FeatureCollection) Append(feature *Feature) *FeatureCollection {
4446
// Items in the ExtraMembers map will be included in the base of the
4547
// feature collection object.
4648
func (fc FeatureCollection) MarshalJSON() ([]byte, error) {
49+
m := newFeatureCollectionDoc(fc)
50+
return marshalJSON(m)
51+
}
52+
53+
// MarshalBSON converts the feature collection object into a BSON document
54+
// represented by bytes. It will handle the encoding of all the child features
55+
// and geometries.
56+
// Items in the ExtraMembers map will be included in the base of the
57+
// feature collection object.
58+
func (fc FeatureCollection) MarshalBSON() ([]byte, error) {
59+
m := newFeatureCollectionDoc(fc)
60+
return bson.Marshal(m)
61+
}
62+
63+
func newFeatureCollectionDoc(fc FeatureCollection) map[string]interface{} {
4764
var tmp map[string]interface{}
4865
if fc.ExtraMembers != nil {
4966
tmp = fc.ExtraMembers.Clone()
@@ -62,7 +79,7 @@ func (fc FeatureCollection) MarshalJSON() ([]byte, error) {
6279
tmp["features"] = fc.Features
6380
}
6481

65-
return marshalJSON(tmp)
82+
return tmp
6683
}
6784

6885
// UnmarshalJSON decodes the data into a GeoJSON feature collection.
@@ -114,6 +131,52 @@ func (fc *FeatureCollection) UnmarshalJSON(data []byte) error {
114131
return nil
115132
}
116133

134+
// UnmarshalBSON will unmarshal a BSON document created with bson.Marshal.
135+
// Extra/foreign members will be put into the `ExtraMembers` attribute.
136+
func (fc *FeatureCollection) UnmarshalBSON(data []byte) error {
137+
tmp := make(map[string]bson.RawValue, 4)
138+
139+
err := bson.Unmarshal(data, &tmp)
140+
if err != nil {
141+
return err
142+
}
143+
144+
*fc = FeatureCollection{}
145+
for key, value := range tmp {
146+
switch key {
147+
case "type":
148+
fc.Type, _ = bson.RawValue(value).StringValueOK()
149+
case "bbox":
150+
err := value.Unmarshal(&fc.BBox)
151+
if err != nil {
152+
return err
153+
}
154+
case "features":
155+
err := value.Unmarshal(&fc.Features)
156+
if err != nil {
157+
return err
158+
}
159+
default:
160+
if fc.ExtraMembers == nil {
161+
fc.ExtraMembers = Properties{}
162+
}
163+
164+
var val interface{}
165+
err := value.Unmarshal(&val)
166+
if err != nil {
167+
return err
168+
}
169+
fc.ExtraMembers[key] = val
170+
}
171+
}
172+
173+
if fc.Type != featureCollection {
174+
return fmt.Errorf("geojson: not a feature collection: type=%s", fc.Type)
175+
}
176+
177+
return nil
178+
}
179+
117180
// UnmarshalFeatureCollection decodes the data into a GeoJSON feature collection.
118181
// Alternately one can call json.Unmarshal(fc) directly for the same result.
119182
func UnmarshalFeatureCollection(data []byte) (*FeatureCollection, error) {

0 commit comments

Comments
 (0)