Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 48 additions & 21 deletions geojson/feature.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"

"github.com/paulmach/orb"
"go.mongodb.org/mongo-driver/bson"
)

// A Feature corresponds to GeoJSON feature object
Expand Down Expand Up @@ -37,19 +38,30 @@ var _ orb.Pointer = &Feature{}
// It will handle the encoding of all the child geometries.
// Alternately one can call json.Marshal(f) directly for the same result.
func (f Feature) MarshalJSON() ([]byte, error) {
jf := &jsonFeature{
return marshalJSON(newFeatureDoc(&f))
}

// MarshalBSON converts the feature object into the proper JSON.
// It will handle the encoding of all the child geometries.
// Alternately one can call json.Marshal(f) directly for the same result.
func (f Feature) MarshalBSON() ([]byte, error) {
return bson.Marshal(newFeatureDoc(&f))
}

func newFeatureDoc(f *Feature) *featureDoc {
doc := &featureDoc{
ID: f.ID,
Type: "Feature",
Properties: f.Properties,
BBox: f.BBox,
Geometry: NewGeometry(f.Geometry),
}

if len(jf.Properties) == 0 {
jf.Properties = nil
if len(doc.Properties) == 0 {
doc.Properties = nil
}

return marshalJSON(jf)
return doc
}

// UnmarshalFeature decodes the data into a GeoJSON feature.
Expand All @@ -67,39 +79,54 @@ func UnmarshalFeature(data []byte) (*Feature, error) {
// UnmarshalJSON handles the correct unmarshalling of the data
// into the orb.Geometry types.
func (f *Feature) UnmarshalJSON(data []byte) error {
jf := &jsonFeature{}
err := unmarshalJSON(data, &jf)
doc := &featureDoc{}
err := unmarshalJSON(data, &doc)
if err != nil {
return err
}

return featureUnmarshalFinish(doc, f)
}

// UnmarshalBSON will unmarshal a BSON document created with bson.Marshal.
func (f *Feature) UnmarshalBSON(data []byte) error {
doc := &featureDoc{}
err := bson.Unmarshal(data, &doc)
if err != nil {
return err
}

if jf.Type != "Feature" {
return fmt.Errorf("geojson: not a feature: type=%s", jf.Type)
return featureUnmarshalFinish(doc, f)
}

func featureUnmarshalFinish(doc *featureDoc, f *Feature) error {
if doc.Type != "Feature" {
return fmt.Errorf("geojson: not a feature: type=%s", doc.Type)
}

var g orb.Geometry
if jf.Geometry != nil {
if jf.Geometry.Coordinates == nil && jf.Geometry.Geometries == nil {
if doc.Geometry != nil {
if doc.Geometry.Coordinates == nil && doc.Geometry.Geometries == nil {
return ErrInvalidGeometry
}
g = jf.Geometry.Geometry()
g = doc.Geometry.Geometry()
}

*f = Feature{
ID: jf.ID,
Type: jf.Type,
Properties: jf.Properties,
BBox: jf.BBox,
ID: doc.ID,
Type: doc.Type,
Properties: doc.Properties,
BBox: doc.BBox,
Geometry: g,
}

return nil
}

type jsonFeature struct {
ID interface{} `json:"id,omitempty"`
Type string `json:"type"`
BBox BBox `json:"bbox,omitempty"`
Geometry *Geometry `json:"geometry"`
Properties Properties `json:"properties"`
type featureDoc struct {
ID interface{} `json:"id,omitempty" bson:"id"`
Type string `json:"type" bson:"type"`
BBox BBox `json:"bbox,omitempty" bson:"bbox,omitempty"`
Geometry *Geometry `json:"geometry" bson:"geometry"`
Properties Properties `json:"properties" bson:"properties"`
}
65 changes: 64 additions & 1 deletion geojson/feature_collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ package geojson

import (
"fmt"

"go.mongodb.org/mongo-driver/bson"
)

const featureCollection = "FeatureCollection"
Expand Down Expand Up @@ -44,6 +46,21 @@ func (fc *FeatureCollection) Append(feature *Feature) *FeatureCollection {
// Items in the ExtraMembers map will be included in the base of the
// feature collection object.
func (fc FeatureCollection) MarshalJSON() ([]byte, error) {
m := newFeatureCollectionDoc(fc)
return marshalJSON(m)
}

// MarshalBSON converts the feature collection object into a BSON document
// represented by bytes. It will handle the encoding of all the child features
// and geometries.
// Items in the ExtraMembers map will be included in the base of the
// feature collection object.
func (fc FeatureCollection) MarshalBSON() ([]byte, error) {
m := newFeatureCollectionDoc(fc)
return bson.Marshal(m)
}

func newFeatureCollectionDoc(fc FeatureCollection) map[string]interface{} {
var tmp map[string]interface{}
if fc.ExtraMembers != nil {
tmp = fc.ExtraMembers.Clone()
Expand All @@ -62,7 +79,7 @@ func (fc FeatureCollection) MarshalJSON() ([]byte, error) {
tmp["features"] = fc.Features
}

return marshalJSON(tmp)
return tmp
}

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

// UnmarshalBSON will unmarshal a BSON document created with bson.Marshal.
// Extra/foreign members will be put into the `ExtraMembers` attribute.
func (fc *FeatureCollection) UnmarshalBSON(data []byte) error {
tmp := make(map[string]bson.RawValue, 4)

err := bson.Unmarshal(data, &tmp)
if err != nil {
return err
}

*fc = FeatureCollection{}
for key, value := range tmp {
switch key {
case "type":
fc.Type, _ = bson.RawValue(value).StringValueOK()
case "bbox":
err := value.Unmarshal(&fc.BBox)
if err != nil {
return err
}
case "features":
err := value.Unmarshal(&fc.Features)
if err != nil {
return err
}
default:
if fc.ExtraMembers == nil {
fc.ExtraMembers = Properties{}
}

var val interface{}
err := value.Unmarshal(&val)
if err != nil {
return err
}
fc.ExtraMembers[key] = val
}
}

if fc.Type != featureCollection {
return fmt.Errorf("geojson: not a feature collection: type=%s", fc.Type)
}

return nil
}

// UnmarshalFeatureCollection decodes the data into a GeoJSON feature collection.
// Alternately one can call json.Unmarshal(fc) directly for the same result.
func UnmarshalFeatureCollection(data []byte) (*FeatureCollection, error) {
Expand Down
Loading