1package geojson 2 3import ( 4 "encoding/binary" 5 "math" 6 "strconv" 7 "unsafe" 8 9 "github.com/tidwall/gjson" 10 "github.com/tidwall/tile38/geojson/geo" 11 "github.com/tidwall/tile38/geojson/poly" 12) 13 14const sizeofPosition = 24 // (X,Y,Z) * 8 15 16// Position is a simple point 17type Position poly.Point 18 19func pointPositions(positions []Position) []poly.Point { 20 return *(*[]poly.Point)(unsafe.Pointer(&positions)) 21} 22func polyPositions(positions []Position) poly.Polygon { 23 return *(*poly.Polygon)(unsafe.Pointer(&positions)) 24} 25func polyMultiPositions(positions [][]Position) []poly.Polygon { 26 return *(*[]poly.Polygon)(unsafe.Pointer(&positions)) 27} 28func polyExteriorHoles(positions [][]Position) (exterior poly.Polygon, holes []poly.Polygon) { 29 switch len(positions) { 30 case 0: 31 case 1: 32 exterior = polyPositions(positions[0]) 33 default: 34 exterior = polyPositions(positions[0]) 35 holes = polyMultiPositions(positions[1:]) 36 } 37 return 38} 39 40func appendPositionJSON(json []byte, p Position, isCordZ bool) []byte { 41 json = strconv.AppendFloat(json, p.X, 'f', -1, 64) 42 json = append(json, ',') 43 json = strconv.AppendFloat(json, p.Y, 'f', -1, 64) 44 if isCordZ { 45 json = append(json, ',') 46 json = strconv.AppendFloat(json, p.Z, 'f', -1, 64) 47 } 48 return json 49} 50 51const earthRadius = 6371e3 52 53func toRadians(deg float64) float64 { return deg * math.Pi / 180 } 54func toDegrees(rad float64) float64 { return rad * 180 / math.Pi } 55 56// DistanceTo calculates the distance to a position 57func (p Position) DistanceTo(position Position) float64 { 58 return geo.DistanceTo(p.Y, p.X, position.Y, position.X) 59} 60 61// Destination calculates a new position based on the distance and bearing. 62func (p Position) Destination(meters, bearingDegrees float64) Position { 63 lat, lon := geo.DestinationPoint(p.Y, p.X, meters, bearingDegrees) 64 return Position{X: lon, Y: lat, Z: 0} 65} 66 67func fillPosition(coords gjson.Result) (Position, error) { 68 var p Position 69 v := coords.Array() 70 switch len(v) { 71 case 0: 72 return p, errInvalidNumberOfPositionValues 73 case 1: 74 if v[0].Type != gjson.Number { 75 return p, errInvalidPositionValue 76 } 77 return p, errInvalidNumberOfPositionValues 78 } 79 for i := 0; i < len(v); i++ { 80 if v[i].Type != gjson.Number { 81 return p, errInvalidPositionValue 82 } 83 } 84 p.X = v[0].Float() 85 p.Y = v[1].Float() 86 if len(v) > 2 { 87 p.Z = v[2].Float() 88 } else { 89 p.Z = nilz 90 } 91 return p, nil 92} 93 94func fillPositionBytes(b []byte, isCordZ bool) (Position, []byte, error) { 95 var p Position 96 if len(b) < 8 { 97 return p, b, errNotEnoughData 98 } 99 p.X = math.Float64frombits(binary.LittleEndian.Uint64(b)) 100 b = b[8:] 101 if len(b) < 8 { 102 return p, b, errNotEnoughData 103 } 104 p.Y = math.Float64frombits(binary.LittleEndian.Uint64(b)) 105 b = b[8:] 106 if isCordZ { 107 if len(b) < 8 { 108 return p, b, errNotEnoughData 109 } 110 p.Z = math.Float64frombits(binary.LittleEndian.Uint64(b)) 111 b = b[8:] 112 } else { 113 p.Z = nilz 114 } 115 return p, b, nil 116} 117 118// ExternalJSON is the simple json representation of the position used for external applications. 119func (p Position) ExternalJSON() string { 120 if p.Z != 0 { 121 return `{"lat":` + strconv.FormatFloat(p.Y, 'f', -1, 64) + `,"lon":` + strconv.FormatFloat(p.X, 'f', -1, 64) + `,"z":` + strconv.FormatFloat(p.Z, 'f', -1, 64) + `}` 122 } 123 return `{"lat":` + strconv.FormatFloat(p.Y, 'f', -1, 64) + `,"lon":` + strconv.FormatFloat(p.X, 'f', -1, 64) + `}` 124} 125