func (c Client) GEOADD(key string, members map[string]GLocation) (newlyAddedMembers map[string]GLocation, err error) {
	newlyAddedMembers = make(map[string]GLocation)
	for member, location := range members {
		builder := newExpresionBuilder()
		builder.updateSetAV(c.skN, location.toAV())
		resp, err := c.ddbClient.UpdateItemRequest(&dynamodb.UpdateItemInput{
			ConditionExpression:       builder.conditionExpression(),
			ExpressionAttributeNames:  builder.expressionAttributeNames(),
			ExpressionAttributeValues: builder.expressionAttributeValues(),
			Key:                       keyDef{pk: key, sk: member}.toAV(c),
			ReturnValues:              dynamodb.ReturnValueAllOld,
			TableName:                 aws.String(c.table),
			UpdateExpression:          builder.updateExpression(),
		}).Send(context.TODO())
		if err != nil {
			return newlyAddedMembers, err
		}
		if len(resp.Attributes) < 1 {
			newlyAddedMembers[member] = location
		}
	}
	return newlyAddedMembers, nil
}
GEOADD adds the given members into the key. Members are represented by a map of name to GLocation, which is just a wrapper
for latitude and longitude. If a member already exists, its location will be updated. The method only returns the members
that were added as part of the operation and did not already exist.

Cost is O(1) / 1 WCU for each member being added or updated.

Works similar to https://redis.io/commands/geoadd
package redimo
import (
	"context"
	"fmt"
	"strconv"
	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/dynamodb"
	"github.com/golang/geo/s1"
	"github.com/golang/geo/s2"
	"github.com/mmcloughlin/geohash"
)
const earthRadiusMeters = 6372797.560856
type GLocation struct {
	Lat float64
	Lon float64
}
func (l GLocation) s2CellID() string {
	return fmt.Sprintf("%d", s2.CellIDFromLatLng(l.s2LatLng()))
}
func (l GLocation) Geohash() string {
	return geohash.Encode(l.Lat, l.Lon)
}
func (l GLocation) toAV() dynamodb.AttributeValue {
	return dynamodb.AttributeValue{
		N: aws.String(l.s2CellID()),
	}
}
func (l *GLocation) setCellIDString(cellIDStr string) {
	cellID, _ := strconv.ParseUint(cellIDStr, 10, 64)
	s2Cell := s2.CellID(cellID)
	s2LatLon := s2Cell.LatLng()
	l.Lat = s2LatLon.Lat.Degrees()
	l.Lon = s2LatLon.Lng.Degrees()
}
func (l GLocation) DistanceTo(other GLocation, unit GUnit) (distance float64) {
	return Meters.To(unit, l.s2LatLng().Distance(other.s2LatLng()).Radians()*earthRadiusMeters)
}
func (l GLocation) s2LatLng() s2.LatLng {
	return s2.LatLngFromDegrees(l.Lat, l.Lon)
}
func fromCellIDString(cellID string) (l GLocation) {
	l.setCellIDString(cellID)
	return
}
type GUnit float64
func (from GUnit) To(to GUnit, d float64) float64 {
	return (d / float64(to)) * float64(from)
}
const (
	Meters     GUnit = 1.0
	Kilometers GUnit = 1000.0
	Miles      GUnit = 1609.34
	Feet       GUnit = 0.3048
)