@@ -15,6 +15,12 @@ type Feature struct {
1515 BBox BBox `json:"bbox,omitempty"`
1616 Geometry orb.Geometry `json:"geometry"`
1717 Properties Properties `json:"properties"`
18+
19+ // ExtraMembers can be used to encoded/decode extra key/members in
20+ // the base of the feature object. Note that keys of "id", "type", "bbox"
21+ // "geometry" and "properties" will not work as those are reserved by the
22+ // GeoJSON spec.
23+ ExtraMembers Properties `json:"-"`
1824}
1925
2026// NewFeature creates and initializes a GeoJSON feature given the required attributes.
@@ -38,31 +44,65 @@ var _ orb.Pointer = &Feature{}
3844// MarshalJSON converts the feature object into the proper JSON.
3945// It will handle the encoding of all the child geometries.
4046// Alternately one can call json.Marshal(f) directly for the same result.
47+ // Items in the ExtraMembers map will be included in the base of the
48+ // feature object.
4149func (f Feature ) MarshalJSON () ([]byte , error ) {
4250 return marshalJSON (newFeatureDoc (& f ))
4351}
4452
4553// MarshalBSON converts the feature object into the proper JSON.
4654// It will handle the encoding of all the child geometries.
4755// Alternately one can call json.Marshal(f) directly for the same result.
56+ // Items in the ExtraMembers map will be included in the base of the
57+ // feature object.
4858func (f Feature ) MarshalBSON () ([]byte , error ) {
4959 return bson .Marshal (newFeatureDoc (& f ))
5060}
5161
52- func newFeatureDoc (f * Feature ) * featureDoc {
53- doc := & featureDoc {
54- ID : f .ID ,
55- Type : "Feature" ,
56- Properties : f .Properties ,
57- BBox : f .BBox ,
58- Geometry : NewGeometry (f .Geometry ),
62+ func newFeatureDoc (f * Feature ) interface {} {
63+ if len (f .ExtraMembers ) == 0 {
64+ doc := & featureDoc {
65+ ID : f .ID ,
66+ Type : "Feature" ,
67+ Properties : f .Properties ,
68+ BBox : f .BBox ,
69+ Geometry : NewGeometry (f .Geometry ),
70+ }
71+
72+ if len (doc .Properties ) == 0 {
73+ doc .Properties = nil
74+ }
75+
76+ return doc
5977 }
6078
61- if len (doc .Properties ) == 0 {
62- doc .Properties = nil
79+ var tmp map [string ]interface {}
80+ if f .ExtraMembers != nil {
81+ tmp = f .ExtraMembers .Clone ()
82+ } else {
83+ tmp = make (map [string ]interface {}, 3 )
84+ }
85+
86+ delete (tmp , "id" )
87+ if f .ID != nil {
88+ tmp ["id" ] = f .ID
89+ }
90+ tmp ["type" ] = "Feature"
91+
92+ delete (tmp , "bbox" )
93+ if f .BBox != nil {
94+ tmp ["bbox" ] = f .BBox
95+ }
96+
97+ tmp ["geometry" ] = NewGeometry (f .Geometry )
98+
99+ if len (f .Properties ) == 0 {
100+ tmp ["properties" ] = nil
101+ } else {
102+ tmp ["properties" ] = f .Properties
63103 }
64104
65- return doc
105+ return tmp
66106}
67107
68108// UnmarshalFeature decodes the data into a GeoJSON feature.
@@ -85,45 +125,122 @@ func (f *Feature) UnmarshalJSON(data []byte) error {
85125 return nil
86126 }
87127
88- doc := & featureDoc {}
89- err := unmarshalJSON (data , & doc )
128+ tmp := make (map [string ]nocopyRawMessage , 4 )
129+
130+ err := unmarshalJSON (data , & tmp )
90131 if err != nil {
91132 return err
92133 }
93134
94- return featureUnmarshalFinish (doc , f )
135+ * f = Feature {}
136+ for key , value := range tmp {
137+ switch key {
138+ case "id" :
139+ err := unmarshalJSON (value , & f .ID )
140+ if err != nil {
141+ return err
142+ }
143+ case "type" :
144+ err := unmarshalJSON (value , & f .Type )
145+ if err != nil {
146+ return err
147+ }
148+ case "bbox" :
149+ err := unmarshalJSON (value , & f .BBox )
150+ if err != nil {
151+ return err
152+ }
153+ case "geometry" :
154+ g := & Geometry {}
155+ err := unmarshalJSON (value , & g )
156+ if err != nil {
157+ return err
158+ }
159+
160+ if g != nil {
161+ f .Geometry = g .Geometry ()
162+ }
163+ case "properties" :
164+ err := unmarshalJSON (value , & f .Properties )
165+ if err != nil {
166+ return err
167+ }
168+ default :
169+ if f .ExtraMembers == nil {
170+ f .ExtraMembers = Properties {}
171+ }
172+
173+ var val interface {}
174+ err := unmarshalJSON (value , & val )
175+ if err != nil {
176+ return err
177+ }
178+ f .ExtraMembers [key ] = val
179+ }
180+ }
181+
182+ if f .Type != "Feature" {
183+ return fmt .Errorf ("geojson: not a feature: type=%s" , f .Type )
184+ }
185+
186+ return nil
95187}
96188
97189// UnmarshalBSON will unmarshal a BSON document created with bson.Marshal.
98190func (f * Feature ) UnmarshalBSON (data []byte ) error {
99- doc := & featureDoc {}
100- err := bson .Unmarshal (data , & doc )
191+ tmp := make (map [string ]bson.RawValue , 4 )
192+
193+ err := bson .Unmarshal (data , & tmp )
101194 if err != nil {
102195 return err
103196 }
104197
105- return featureUnmarshalFinish (doc , f )
106- }
107-
108- func featureUnmarshalFinish (doc * featureDoc , f * Feature ) error {
109- if doc .Type != "Feature" {
110- return fmt .Errorf ("geojson: not a feature: type=%s" , doc .Type )
111- }
112-
113- var g orb.Geometry
114- if doc .Geometry != nil {
115- if doc .Geometry .Coordinates == nil && doc .Geometry .Geometries == nil {
116- return ErrInvalidGeometry
198+ * f = Feature {}
199+ for key , value := range tmp {
200+ switch key {
201+ case "id" :
202+ err := value .Unmarshal (& f .ID )
203+ if err != nil {
204+ return err
205+ }
206+ case "type" :
207+ f .Type , _ = bson .RawValue (value ).StringValueOK ()
208+ case "bbox" :
209+ err := value .Unmarshal (& f .BBox )
210+ if err != nil {
211+ return err
212+ }
213+ case "geometry" :
214+ g := & Geometry {}
215+ err := value .Unmarshal (& g )
216+ if err != nil {
217+ return err
218+ }
219+
220+ if g != nil {
221+ f .Geometry = g .Geometry ()
222+ }
223+ case "properties" :
224+ err := value .Unmarshal (& f .Properties )
225+ if err != nil {
226+ return err
227+ }
228+ default :
229+ if f .ExtraMembers == nil {
230+ f .ExtraMembers = Properties {}
231+ }
232+
233+ var val interface {}
234+ err := value .Unmarshal (& val )
235+ if err != nil {
236+ return err
237+ }
238+ f .ExtraMembers [key ] = val
117239 }
118- g = doc .Geometry .Geometry ()
119240 }
120241
121- * f = Feature {
122- ID : doc .ID ,
123- Type : doc .Type ,
124- Properties : doc .Properties ,
125- BBox : doc .BBox ,
126- Geometry : g ,
242+ if f .Type != "Feature" {
243+ return fmt .Errorf ("geojson: not a feature: type=%s" , f .Type )
127244 }
128245
129246 return nil
0 commit comments