aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/paulmach/orb/geojson/feature.go
blob: 68b1f8fc315a05edad5e5e1fd93d93f4ab8aa0c2 (plain) (blame)
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
package geojson

import (
	"fmt"

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

// A Feature corresponds to GeoJSON feature object
type Feature struct {
	ID         interface{}  `json:"id,omitempty"`
	Type       string       `json:"type"`
	BBox       BBox         `json:"bbox,omitempty"`
	Geometry   orb.Geometry `json:"geometry"`
	Properties Properties   `json:"properties"`
}

// NewFeature creates and initializes a GeoJSON feature given the required attributes.
func NewFeature(geometry orb.Geometry) *Feature {
	return &Feature{
		Type:       "Feature",
		Geometry:   geometry,
		Properties: make(map[string]interface{}),
	}
}

// Point implements the orb.Pointer interface so that Features can be used
// with quadtrees. The point returned is the center of the Bound of the geometry.
// To represent the geometry with another point you must create a wrapper type.
func (f *Feature) Point() orb.Point {
	return f.Geometry.Bound().Center()
}

var _ orb.Pointer = &Feature{}

// MarshalJSON 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) MarshalJSON() ([]byte, error) {
	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(doc.Properties) == 0 {
		doc.Properties = nil
	}

	return doc
}

// UnmarshalFeature decodes the data into a GeoJSON feature.
// Alternately one can call json.Unmarshal(f) directly for the same result.
func UnmarshalFeature(data []byte) (*Feature, error) {
	f := &Feature{}
	err := f.UnmarshalJSON(data)
	if err != nil {
		return nil, err
	}

	return f, nil
}

// UnmarshalJSON handles the correct unmarshalling of the data
// into the orb.Geometry types.
func (f *Feature) UnmarshalJSON(data []byte) error {
	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
	}

	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 doc.Geometry != nil {
		if doc.Geometry.Coordinates == nil && doc.Geometry.Geometries == nil {
			return ErrInvalidGeometry
		}
		g = doc.Geometry.Geometry()
	}

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

	return nil
}

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"`
}