Skip to content

Latest commit

 

History

History
161 lines (123 loc) · 5.16 KB

File metadata and controls

161 lines (123 loc) · 5.16 KB

orb/geojson Godoc Reference

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.

Unmarshalling (JSON -> Go)

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)

Marshalling (Go -> JSON)

fc := geojson.NewFeatureCollection()
fc.Append(geojson.NewFeature(orb.Point{1, 2}))

rawJSON, _ := fc.MarshalJSON()

// or
blob, _ := json.Marshal(fc)

Foreign/extra members in feature and feature collection

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.

Performance

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 = c

The 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%

Feature Properties

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) string

Generic Properties

Go 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"