Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 4 additions & 30 deletions pkg/models/geo.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,12 @@ import (
)

const (
// TimeFormatRFC3339 is the string used for RFC3339
TimeFormatRFC3339 = "RFC3339"
minLat = -90.0
maxLat = 90.0
minLng = -180.0
maxLng = 180.0
UnitsM = "M"
ReferenceW84 = "W84"
minLat = -90.0
maxLat = 90.0
minLng = -180.0
maxLng = 180.0
)

var (
unitToMeterMultiplicativeFactors = map[unit]float32{
unitMeter: 1,
}

altitudeReferenceWGS84 altitudeReference = "W84"
unitMeter unit = "M"
)

type (
altitudeReference string
unit string
)

func (ar altitudeReference) String() string {
return string(ar)
}

func (u unit) String() string {
return string(u)
}

func float32p(v float32) *float32 {
return &v
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/scd/constraints_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -369,10 +369,10 @@ func validateAndReturnConstraintUpsertParams(

// Start and end times are required for each volume
// The end time may not be in the past
valid.uExtent, err = dssmodels.UnionVolumes4DFromSCDRest(
valid.uExtent, err = scdmodels.UnionVolumes4DFromSCDRest(
params.Extents,
dssmodels.WithRequireTimeBounds(),
dssmodels.WithRequireEndTimeAfter(now),
scdmodels.WithRequireTimeBounds(),
scdmodels.WithRequireEndTimeAfter(now),
)
if err != nil {
return nil, stacktrace.Propagate(err, "Invalid extents")
Expand Down Expand Up @@ -404,7 +404,7 @@ func (a *Server) QueryConstraintReferences(ctx context.Context, req *restapi.Que
}

// Parse area of interest to common Volume4D
vol4, err := dssmodels.Volume4DFromSCDRest(aoi)
vol4, err := scdmodels.Volume4DFromSCDRest(aoi)
if err != nil {
return restapi.QueryConstraintReferencesResponseSet{Response400: &restapi.ErrorResponse{
Message: dsserr.Handle(ctx, stacktrace.PropagateWithCode(err, dsserr.BadRequest, "Failed to convert to internal geometry model"))}}
Expand Down
4 changes: 2 additions & 2 deletions pkg/scd/models/constraints.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@ func (c *Constraint) ToRest() *restapi.ConstraintReference {
if c.StartTime != nil {
result.TimeStart = restapi.Time{
Value: c.StartTime.Format(time.RFC3339Nano),
Format: dssmodels.TimeFormatRFC3339,
Format: TimeFormatRFC3339,
}
}

if c.EndTime != nil {
result.TimeEnd = restapi.Time{
Value: c.EndTime.Format(time.RFC3339Nano),
Format: dssmodels.TimeFormatRFC3339,
Format: TimeFormatRFC3339,
}
}

Expand Down
52 changes: 30 additions & 22 deletions pkg/models/scd_conversions.go → pkg/scd/models/conversions.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,21 @@ import (
"time"

restapi "github.com/interuss/dss/pkg/api/scdv1"
dssmodels "github.com/interuss/dss/pkg/models"
"github.com/interuss/stacktrace"
)

type Volume4DValidator func(*Volume4D) error
const (
// TimeFormatRFC3339 is the string used for RFC3339
TimeFormatRFC3339 = "RFC3339"
UnitsM = "M"
ReferenceW84 = "W84"
)

type Volume4DValidator func(*dssmodels.Volume4D) error

func WithRequireTimeBounds() Volume4DValidator {
return func(v *Volume4D) error {
return func(v *dssmodels.Volume4D) error {
if v.StartTime == nil {
return stacktrace.NewError("Missing start time")
}
Expand All @@ -22,7 +30,7 @@ func WithRequireTimeBounds() Volume4DValidator {
}

func WithRequireEndTimeAfter(now time.Time) Volume4DValidator {
return func(v *Volume4D) error {
return func(v *dssmodels.Volume4D) error {
if v.EndTime != nil && v.EndTime.Before(now) {
return stacktrace.NewError("End time may not be in the past")
}
Expand All @@ -31,7 +39,7 @@ func WithRequireEndTimeAfter(now time.Time) Volume4DValidator {
}

func WithRequireAltitudeBounds() Volume4DValidator {
return func(v *Volume4D) error {
return func(v *dssmodels.Volume4D) error {
if v.SpatialVolume.AltitudeLo == nil {
return stacktrace.NewError("Missing lower altitude")
}
Expand All @@ -44,16 +52,16 @@ func WithRequireAltitudeBounds() Volume4DValidator {

// UnionVolumes4DFromSCDRest converts a slice of vol4 SCD v1 REST model to a single bounding Volume4D
// Validation is applied on the resulting volume union
func UnionVolumes4DFromSCDRest(vol4s []restapi.Volume4D, validators ...Volume4DValidator) (*Volume4D, error) {
volumes := make([]*Volume4D, len(vol4s))
func UnionVolumes4DFromSCDRest(vol4s []restapi.Volume4D, validators ...Volume4DValidator) (*dssmodels.Volume4D, error) {
volumes := make([]*dssmodels.Volume4D, len(vol4s))
for idx, vol4 := range vol4s {
volume, err := Volume4DFromSCDRest(&vol4)
if err != nil {
return nil, stacktrace.Propagate(err, "Failed to parse volume %d", idx)
}
volumes[idx] = volume
}
union, err := UnionVolumes4D(volumes...)
union, err := dssmodels.UnionVolumes4D(volumes...)
if err != nil {
return nil, stacktrace.Propagate(err, "Failed to union volumes")
}
Expand All @@ -68,7 +76,7 @@ func UnionVolumes4DFromSCDRest(vol4s []restapi.Volume4D, validators ...Volume4DV
}

// Volume4DFromSCDRest converts vol4 SCD v1 REST model to a Volume4D
func Volume4DFromSCDRest(vol4 *restapi.Volume4D) (*Volume4D, error) {
func Volume4DFromSCDRest(vol4 *restapi.Volume4D) (*dssmodels.Volume4D, error) {
vol3, err := Volume3DFromSCDRest(&vol4.Volume)
if err != nil {
return nil, err // No need to Propagate this error as this stack layer does not add useful information
Expand Down Expand Up @@ -96,7 +104,7 @@ func Volume4DFromSCDRest(vol4 *restapi.Volume4D) (*Volume4D, error) {
return nil, stacktrace.NewError("Start time cannot be after end time")
}

volume := &Volume4D{
volume := &dssmodels.Volume4D{
SpatialVolume: vol3,
StartTime: startTime,
EndTime: endTime,
Expand All @@ -106,7 +114,7 @@ func Volume4DFromSCDRest(vol4 *restapi.Volume4D) (*Volume4D, error) {
}

// Volume3DFromSCDRest converts a vol3 SCD v1 REST model to a Volume3D
func Volume3DFromSCDRest(vol3 *restapi.Volume3D) (*Volume3D, error) {
func Volume3DFromSCDRest(vol3 *restapi.Volume3D) (*dssmodels.Volume3D, error) {
if vol3 == nil {
return nil, nil
}
Expand All @@ -119,7 +127,7 @@ func Volume3DFromSCDRest(vol3 *restapi.Volume3D) (*Volume3D, error) {
if vol3.AltitudeLower.Reference != ReferenceW84 {
return nil, stacktrace.NewError("Invalid lower altitude reference")
}
altLo = float32p(float32(vol3.AltitudeLower.Value))
altLo = new(float32(vol3.AltitudeLower.Value))
}

var altHi *float32
Expand All @@ -130,7 +138,7 @@ func Volume3DFromSCDRest(vol3 *restapi.Volume3D) (*Volume3D, error) {
if vol3.AltitudeUpper.Reference != ReferenceW84 {
return nil, stacktrace.NewError("Invalid upper altitude reference")
}
altHi = float32p(float32(vol3.AltitudeUpper.Value))
altHi = new(float32(vol3.AltitudeUpper.Value))
}

if altLo != nil && altHi != nil && *altLo > *altHi {
Expand All @@ -141,36 +149,36 @@ func Volume3DFromSCDRest(vol3 *restapi.Volume3D) (*Volume3D, error) {
case vol3.OutlineCircle != nil && vol3.OutlinePolygon != nil:
return nil, stacktrace.NewError("Both circle and polygon specified in outline geometry")
case vol3.OutlinePolygon != nil:
return &Volume3D{
return &dssmodels.Volume3D{
Footprint: GeoPolygonFromSCDRest(vol3.OutlinePolygon),
AltitudeLo: altLo,
AltitudeHi: altHi,
}, nil
case vol3.OutlineCircle != nil:
return &Volume3D{
return &dssmodels.Volume3D{
Footprint: GeoCircleFromSCDRest(vol3.OutlineCircle),
AltitudeLo: altLo,
AltitudeHi: altHi,
}, nil
}

return &Volume3D{
return &dssmodels.Volume3D{
AltitudeLo: altLo,
AltitudeHi: altHi,
}, nil
}

// GeoCircleFromSCDRest converts a circle SCD v1 REST model to a GeoCircle
func GeoCircleFromSCDRest(c *restapi.Circle) *GeoCircle {
return &GeoCircle{
func GeoCircleFromSCDRest(c *restapi.Circle) *dssmodels.GeoCircle {
return &dssmodels.GeoCircle{
Center: *LatLngPointFromSCDRest(c.Center),
RadiusMeter: unitToMeterMultiplicativeFactors[unit(c.Radius.Units)] * c.Radius.Value,
RadiusMeter: c.Radius.Value,
}
}

// GeoPolygonFromSCDRest converts a polygon SCD v1 REST model to a GeoPolygon
func GeoPolygonFromSCDRest(p *restapi.Polygon) *GeoPolygon {
result := &GeoPolygon{}
func GeoPolygonFromSCDRest(p *restapi.Polygon) *dssmodels.GeoPolygon {
result := &dssmodels.GeoPolygon{}
for _, ltlng := range p.Vertices {
result.Vertices = append(result.Vertices, LatLngPointFromSCDRest(&ltlng))
}
Expand All @@ -179,8 +187,8 @@ func GeoPolygonFromSCDRest(p *restapi.Polygon) *GeoPolygon {
}

// LatLngPointFromSCDRest converts a point SCD v1 REST model to a latlngpoint
func LatLngPointFromSCDRest(p *restapi.LatLngPoint) *LatLngPoint {
return &LatLngPoint{
func LatLngPointFromSCDRest(p *restapi.LatLngPoint) *dssmodels.LatLngPoint {
return &dssmodels.LatLngPoint{
Lat: float64(p.Lat),
Lng: float64(p.Lng),
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"time"

restapi "github.com/interuss/dss/pkg/api/scdv1"
dssmodels "github.com/interuss/dss/pkg/models"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
Expand All @@ -30,7 +31,7 @@ func TestUnionVolumes4DFromSCDRest(t *testing.T) {
name string
validators []Volume4DValidator
rest []restapi.Volume4D
want *Volume4D
want *dssmodels.Volume4D
wantErr bool
}{
{
Expand All @@ -43,7 +44,7 @@ func TestUnionVolumes4DFromSCDRest(t *testing.T) {
{TimeStart: newRestTime(timeStart), TimeEnd: newRestTime(timeMid)},
{TimeStart: newRestTime(timeStart), TimeEnd: newRestTime(timeEnd)},
},
want: &Volume4D{SpatialVolume: &Volume3D{}, StartTime: &timeStart, EndTime: &timeEnd},
want: &dssmodels.Volume4D{SpatialVolume: &dssmodels.Volume3D{}, StartTime: &timeStart, EndTime: &timeEnd},
},
{
name: "TimeEndExpired",
Expand Down Expand Up @@ -79,7 +80,7 @@ func TestUnionVolumes4DFromSCDRest(t *testing.T) {
{Volume: restapi.Volume3D{AltitudeLower: newRestAlt(altLo), AltitudeUpper: newRestAlt(altMid)}},
{Volume: restapi.Volume3D{AltitudeLower: newRestAlt(altMid), AltitudeUpper: newRestAlt(altHi)}},
},
want: &Volume4D{SpatialVolume: &Volume3D{AltitudeLo: &altLo, AltitudeHi: &altHi}},
want: &dssmodels.Volume4D{SpatialVolume: &dssmodels.Volume3D{AltitudeLo: &altLo, AltitudeHi: &altHi}},
},
{
name: "MissingLowerAltitude",
Expand Down Expand Up @@ -122,18 +123,18 @@ func TestVolume4DFromSCDRest(t *testing.T) {
testCases := []struct {
name string
rest *restapi.Volume4D
want *Volume4D
want *dssmodels.Volume4D
wantErr bool
}{
{
name: "Empty",
rest: &restapi.Volume4D{},
want: &Volume4D{SpatialVolume: &Volume3D{}},
want: &dssmodels.Volume4D{SpatialVolume: &dssmodels.Volume3D{}},
},
{
name: "Times",
rest: &restapi.Volume4D{TimeStart: newRestTime(start), TimeEnd: newRestTime(end)},
want: &Volume4D{SpatialVolume: &Volume3D{}, StartTime: &start, EndTime: &end},
want: &dssmodels.Volume4D{SpatialVolume: &dssmodels.Volume3D{}, StartTime: &start, EndTime: &end},
},
{
name: "InvalidTimeStart",
Expand Down Expand Up @@ -173,21 +174,21 @@ func TestVolume3DFromSCDRest(t *testing.T) {
testCases := []struct {
name string
rest *restapi.Volume3D
want *Volume3D
want *dssmodels.Volume3D
wantErr bool
}{
{
name: "Empty",
rest: &restapi.Volume3D{},
want: &Volume3D{},
want: &dssmodels.Volume3D{},
},
{
name: "Polygon",
rest: &restapi.Volume3D{
OutlinePolygon: &restapi.Polygon{},
},
want: &Volume3D{
Footprint: &GeoPolygon{},
want: &dssmodels.Volume3D{
Footprint: &dssmodels.GeoPolygon{},
},
},
{
Expand All @@ -198,14 +199,14 @@ func TestVolume3DFromSCDRest(t *testing.T) {
Radius: &restapi.Radius{},
},
},
want: &Volume3D{
Footprint: &GeoCircle{},
want: &dssmodels.Volume3D{
Footprint: &dssmodels.GeoCircle{},
},
},
{
name: "Altitudes",
rest: &restapi.Volume3D{AltitudeLower: newRestAlt(lo), AltitudeUpper: newRestAlt(hi)},
want: &Volume3D{AltitudeLo: &lo, AltitudeHi: &hi},
want: &dssmodels.Volume3D{AltitudeLo: &lo, AltitudeHi: &hi},
},
{
name: "InvalidLowerAltitude",
Expand Down
4 changes: 2 additions & 2 deletions pkg/scd/models/operational_intents.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,14 @@ func (o *OperationalIntent) ToRest() *restapi.OperationalIntentReference {
if o.StartTime != nil {
result.TimeStart = restapi.Time{
Value: o.StartTime.Format(time.RFC3339Nano),
Format: dssmodels.TimeFormatRFC3339,
Format: TimeFormatRFC3339,
}
}

if o.EndTime != nil {
result.TimeEnd = restapi.Time{
Value: o.EndTime.Format(time.RFC3339Nano),
Format: dssmodels.TimeFormatRFC3339,
Format: TimeFormatRFC3339,
}
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/scd/models/subscriptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,14 @@ func (s *Subscription) ToRest(dependentOperationalIntents []dssmodels.ID) (*rest
if s.StartTime != nil {
result.TimeStart = &restapi.Time{
Value: s.StartTime.Format(time.RFC3339Nano),
Format: dssmodels.TimeFormatRFC3339,
Format: TimeFormatRFC3339,
}
}

if s.EndTime != nil {
result.TimeEnd = &restapi.Time{
Value: s.EndTime.Format(time.RFC3339Nano),
Format: dssmodels.TimeFormatRFC3339,
Format: TimeFormatRFC3339,
}
}

Expand Down
Loading
Loading