-
Notifications
You must be signed in to change notification settings - Fork 100
Expand file tree
/
Copy pathidentification_service_area.go
More file actions
141 lines (126 loc) · 4.21 KB
/
Copy pathidentification_service_area.go
File metadata and controls
141 lines (126 loc) · 4.21 KB
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
133
134
135
136
137
138
139
140
141
package models
import (
"time"
"github.com/golang/geo/s2"
"github.com/golang/protobuf/ptypes"
"github.com/interuss/dss/pkg/api/v1/ridpb"
dsserr "github.com/interuss/dss/pkg/errors"
"github.com/interuss/dss/pkg/geo"
dssmodels "github.com/interuss/dss/pkg/models"
"github.com/interuss/stacktrace"
"google.golang.org/protobuf/proto"
)
// IdentificationServiceArea represents a USS ISA over a given 4D volume.
type IdentificationServiceArea struct {
ID dssmodels.ID
URL string
Owner dssmodels.Owner
Cells s2.CellUnion
StartTime *time.Time
EndTime *time.Time
Version *dssmodels.Version
AltitudeHi *float32
AltitudeLo *float32
Writer string
}
// SetCells is a convenience function that accepts an int64 array and converts
// to s2.CellUnion.
// TODO: wrap s2.CellUnion in a custom type that embeds the struct such that
// we can still call its function directly, but also implements scan for sql
// driver.
func (i *IdentificationServiceArea) SetCells(cids []int64) {
i.Cells = geo.CellUnionFromInt64(cids)
}
// ToProto converts an IdentificationServiceArea struct to an
// IdentificationServiceArea proto for API consumption.
func (i *IdentificationServiceArea) ToProto() (*ridpb.IdentificationServiceArea, error) {
result := &ridpb.IdentificationServiceArea{
Id: i.ID.String(),
Owner: i.Owner.String(),
FlightsUrl: i.URL,
Version: i.Version.String(),
}
if i.StartTime != nil {
ts, err := ptypes.TimestampProto(*i.StartTime)
if err != nil {
return nil, stacktrace.Propagate(err, "Error converting start time to proto")
}
result.TimeStart = ts
}
if i.EndTime != nil {
ts, err := ptypes.TimestampProto(*i.EndTime)
if err != nil {
return nil, stacktrace.Propagate(err, "Error converting end time to proto")
}
result.TimeEnd = ts
}
return result, nil
}
// SetExtents performs some data validation and sets the 4D volume on the
// IdentificationServiceArea.
func (i *IdentificationServiceArea) SetExtents(extents *ridpb.Volume4D) error {
var err error
if extents == nil {
return nil
}
if startTime := extents.GetTimeStart(); startTime != nil {
ts, err := ptypes.Timestamp(startTime)
if err != nil {
return stacktrace.Propagate(err, "Error converting start time from proto")
}
i.StartTime = &ts
}
if endTime := extents.GetTimeEnd(); endTime != nil {
ts, err := ptypes.Timestamp(endTime)
if err != nil {
return stacktrace.Propagate(err, "Error converting end time from proto")
}
i.EndTime = &ts
}
space := extents.GetSpatialVolume()
if space == nil {
return stacktrace.NewError("Missing required spatial_volume")
}
i.AltitudeHi = proto.Float32(space.GetAltitudeHi())
i.AltitudeLo = proto.Float32(space.GetAltitudeLo())
footprint := space.GetFootprint()
if footprint == nil {
return stacktrace.NewError("spatial_volume missing required footprint")
}
i.Cells, err = dssmodels.GeoPolygonFromRIDProto(footprint).CalculateCovering()
if err != nil {
return stacktrace.Propagate(err, "Error calculating covering from polygon")
}
return nil
}
// AdjustTimeRange adjusts the time range to the max allowed ranges on a
// IdentificationServiceArea.
func (i *IdentificationServiceArea) AdjustTimeRange(now time.Time, old *IdentificationServiceArea) error {
if i.StartTime == nil {
// If StartTime was omitted, default to Now() for new ISAs or re-
// use the existing time of existing ISAs.
if old == nil {
i.StartTime = &now
} else {
i.StartTime = old.StartTime
}
} else {
// If setting the StartTime explicitly ensure it is not too far in the past.
if now.Sub(*i.StartTime) > maxClockSkew {
return stacktrace.NewErrorWithCode(dsserr.BadRequest, "IdentificationServiceArea time_start must not be in the past")
}
}
// If EndTime was omitted default to the existing ISA's EndTime.
if i.EndTime == nil && old != nil {
i.EndTime = old.EndTime
}
// EndTime cannot be omitted for new ISAs.
if i.EndTime == nil {
return stacktrace.NewErrorWithCode(dsserr.BadRequest, "IdentificationServiceArea must have an time_end")
}
// EndTime cannot be before StartTime.
if i.EndTime.Sub(*i.StartTime) < 0 {
return stacktrace.NewErrorWithCode(dsserr.BadRequest, "IdentificationServiceArea time_end must be after time_start")
}
return nil
}