This package encodes and decodes GeoJSON into Go structs using the geometries in the orb package.
Generics are supported for Feature Properties, but the default is map[string]any.
See the Generic Properties section below for more information.
Supports both the json.Marshaler and
json.Unmarshaler interfaces.
The package also provides helper functions such as UnmarshalFeatureCollection and UnmarshalFeature.
The types also support BSON via the bson.Marshaler and bson.Unmarshaler interfaces. These types can be used directly when working with MongoDB.
rawJSON := []byte(`
{ "type": "FeatureCollection",
"features": [
{ "type": "Feature",
"geometry": {"type": "Point", "coordinates": [102.0, 0.5]},
"properties": {"prop0": "value0"}
}
]
}`)
fc, _ := geojson.UnmarshalFeatureCollection(rawJSON)
// or
fc := geojson.NewFeatureCollection()
err := json.Unmarshal(rawJSON, &fc)
// Geometry will be unmarshalled into the correct geo.Geometry type.
point := fc.Features[0].Geometry.(orb.Point)fc := geojson.NewFeatureCollection()
fc.Append(geojson.NewFeature(orb.Point{1, 2}))
rawJSON, _ := fc.MarshalJSON()
// or
blob, _ := json.Marshal(fc)rawJSON := []byte(`
{ "type": "FeatureCollection",
"generator": "myapp",
"timestamp": "2020-06-15T01:02:03Z",
"features": [
{ "type": "Feature",
"geometry": {"type": "Point", "coordinates": [102.0, 0.5]},
"properties": {"prop0": "value0"}
}
]
}`)
fc, _ := geojson.UnmarshalFeatureCollection(rawJSON)
fc.ExtraMembers["generator"] // == "myApp"
fc.ExtraMembers["timestamp"] // == "2020-06-15T01:02:03Z"
// Marshalling will include values in `ExtraMembers` in the
// base featureCollection object.For performance critical applications, consider a third party replacement of "encoding/json" like github.com/json-iterator/go
This can be enabled with something like this:
import (
jsoniter "github.com/json-iterator/go"
"github.com/paulmach/orb/geojson"
)
// in an init() or main(), etc.
c := jsoniter.Config{
EscapeHTML: true,
SortMapKeys: false,
MarshalFloatWith6Digits: true,
}.Froze()
geojson.CustomJSONMarshaler = c
geojson.CustomJSONUnmarshaler = cThe above change can have dramatic performance implications, see the benchmarks below on a 100k feature collection file:
benchmark old ns/op new ns/op delta
BenchmarkFeatureMarshalJSON-12 2694543 733480 -72.78%
BenchmarkFeatureUnmarshalJSON-12 5383825 2738183 -49.14%
BenchmarkGeometryMarshalJSON-12 210107 62789 -70.12%
BenchmarkGeometryUnmarshalJSON-12 691472 144689 -79.08%
benchmark old allocs new allocs delta
BenchmarkFeatureMarshalJSON-12 7818 2316 -70.38%
BenchmarkFeatureUnmarshalJSON-12 23047 31946 +38.61%
BenchmarkGeometryMarshalJSON-12 2 3 +50.00%
BenchmarkGeometryUnmarshalJSON-12 2042 18 -99.12%
benchmark old bytes new bytes delta
BenchmarkFeatureMarshalJSON-12 794088 490251 -38.26%
BenchmarkFeatureUnmarshalJSON-12 766354 1068497 +39.43%
BenchmarkGeometryMarshalJSON-12 24787 18650 -24.76%
BenchmarkGeometryUnmarshalJSON-12 79784 51374 -35.61%
GeoJSON features can have properties of any type. This can cause issues in a statically typed
language such as Go. Included is a Properties type with some helper methods that will try to
force convert a property. An optional default, will be used if the property is missing or the wrong
type.
f.Properties.MustBool(key string, def ...bool) bool
f.Properties.MustFloat64(key string, def ...float64) float64
f.Properties.MustInt(key string, def ...int) int
f.Properties.MustString(key string, def ...string) stringGo 1.18 introduced generics, and with it the ability to have a custom type for feature properties.
type MyProperties struct {
Name string `json:"name"`
Age int `json:"age"`
}
fc := geojson.FeatureCollectionOf[MyProperties]{}
fc.Append(
&geojson.FeatureOf[MyProperties]{
Geometry: orb.Point{1, 2},
Properties: MyProperties{Name: "Alice", Age: 30},
},
)
// unmarshalling can be even easier
fc2 := geojson.FeatureCollectionOf[MyProperties]{}
err := json.Unmarshal(rawJSON, &fc2)
fc2.Features[0].Properties.Name // == "Alice"