diff --git a/ats_schema_test.go b/ats_schema_test.go index cdce40c..54d1bf9 100644 --- a/ats_schema_test.go +++ b/ats_schema_test.go @@ -409,9 +409,8 @@ func TestATSSchemaBundleMatchesOGC(t *testing.T) { // reason is the standard's, not the tier's: `temporalPrimitiveValue` declares // `datetimes` an array of `minItems: 2` beside `values` as a single scalar // (`oneOf` number/string/boolean), so no document carrying a value per instant -// satisfies it; and its `interpolation` enum reads Discrete/Step/Linear/Regression -// where the temporal-geometry half of the same document names Stepwise. This -// asserts the cell that has one authority, which is the type token. +// satisfies it. This asserts the cells that are satisfiable, of which the type +// token is one. func TestATSSchemaTemporalPropertyType(t *testing.T) { f, err := os.Open(ogcBundlePath) if err != nil { @@ -465,3 +464,78 @@ func TestATSSchemaTemporalPropertyType(t *testing.T) { } } } + +// The interpolation the tier writes is one the standard names. +// +// ⛔ BOTH ADMITTED SETS ARE READ OUT OF THE VENDORED DOCUMENT. Part 1 constrains +// an interpolation in exactly two places and they are not the same list: +// `motionCurve` (what a temporal geometry takes, through a $ref that a walk over +// `temporalPrimitiveGeometry` alone does not see) and `temporalPrimitiveValue`. +// The step function is `Step` in both, and the word `Stepwise` — the older +// MF-JSON encoding extension's spelling — appears nowhere in the document. +func TestATSSchemaInterpolationToken(t *testing.T) { + raw, err := os.ReadFile(ogcBundlePath) + if err != nil { + t.Fatal(err) + } + if n := bytes.Count(raw, []byte(`"Stepwise"`)); n != 0 { + t.Errorf(`the vendored document names "Stepwise" %d times; the tier writes "Step" because it named it 0`, n) + } + var doc struct { + Components struct { + Schemas struct { + MotionCurve struct { + OneOf []struct { + Enum []string `json:"enum"` + } `json:"oneOf"` + } `json:"motionCurve"` + TemporalPrimitiveValue struct { + Properties struct { + Interpolation struct { + Enum []string `json:"enum"` + } `json:"interpolation"` + } `json:"properties"` + } `json:"temporalPrimitiveValue"` + } `json:"schemas"` + } `json:"components"` + } + if err := json.Unmarshal(raw, &doc); err != nil { + t.Fatalf("reading %s: %v", ogcBundlePath, err) + } + var curve []string + for _, b := range doc.Components.Schemas.MotionCurve.OneOf { + curve = append(curve, b.Enum...) + } + value := doc.Components.Schemas.TemporalPrimitiveValue.Properties.Interpolation.Enum + if len(curve) == 0 || len(value) == 0 { + t.Fatal("the vendored document declares no interpolation enum, so this test would assert nothing") + } + in := func(set []string, s string) bool { + for _, a := range set { + if a == s { + return true + } + } + return false + } + // What the tier writes: MobilityDB's own token, carried through ogcify. + const geometry = `{"type":"MovingPoint","interpolation":"Step"}` + const valueSeq = `{"interpolation":"Step","values":[1,2]}` + for _, c := range []struct { + what, doc string + admitted []string + }{ + {"a temporal geometry", geometry, curve}, + {"a temporal property value", valueSeq, value}, + } { + var m map[string]any + if err := json.Unmarshal([]byte(ogcify(c.doc)), &m); err != nil { + t.Fatalf("%s: ogcify produced no JSON: %v", c.what, err) + } + got, _ := m["interpolation"].(string) + if !in(c.admitted, got) { + t.Errorf("%s is written with interpolation %q, which the standard does not name; it admits %v", + c.what, got, c.admitted) + } + } +} diff --git a/main.go b/main.go index 09e4509..ee49235 100644 --- a/main.go +++ b/main.go @@ -42,8 +42,18 @@ var ( ) // OGC <-> MobilityDB conventions (assessed against live MobilityDB): -// MobilityDB rejects "Stepwise" and uses "Step"; crs is "EPSG:" vs the URN. -var ogc2mdbInterp = map[string]string{"Linear": "Linear", "Stepwise": "Step", "Discrete": "Discrete"} +// crs is "EPSG:" vs the URN. +// +// The step function is "Step" on both sides. OGC API - Moving Features Part 1 +// names it so in each of the two places it constrains an interpolation — +// `motionCurve` (Discrete/Step/Linear/Quadratic/Cubic, what a temporal geometry +// takes) and `temporalPrimitiveValue` (Discrete/Step/Linear/Regression) — and +// the word "Stepwise" appears nowhere in that standard. "Stepwise" belongs to +// the older MF-JSON encoding extension, and is accepted on input for a client +// that still writes it. +var ogc2mdbInterp = map[string]string{ + "Linear": "Linear", "Step": "Step", "Discrete": "Discrete", "Stepwise": "Step", +} var epsgName = regexp.MustCompile(`"name":\s*"EPSG:(\d+)"`) var epsgURN = regexp.MustCompile(`EPSG:+(\d+)`) @@ -55,9 +65,10 @@ var hourOnlyOffset = regexp.MustCompile(`(\d\d:\d\d:\d\d(?:\.\d+)?)([+-]\d\d)([" // rfc3339Tz completes hour-only timezone offsets so datetimes are RFC 3339. func rfc3339Tz(s string) string { return hourOnlyOffset.ReplaceAllString(s, `$1$2:00$3`) } +// ogcify turns what MobilityDB writes into what the standard names. The +// interpolation needs nothing: MobilityDB's "Step" is already the token both +// `motionCurve` and `temporalPrimitiveValue` enumerate. func ogcify(s string) string { - s = strings.ReplaceAll(s, `"interpolation": "Step"`, `"interpolation": "Stepwise"`) - s = strings.ReplaceAll(s, `"interpolation":"Step"`, `"interpolation":"Stepwise"`) s = rfc3339Tz(s) return epsgName.ReplaceAllString(s, `"name":"urn:ogc:def:crs:EPSG::$1"`) } diff --git a/samples/temporal-geometry-velocity.json b/samples/temporal-geometry-velocity.json index 0f53018..eca97e8 100644 --- a/samples/temporal-geometry-velocity.json +++ b/samples/temporal-geometry-velocity.json @@ -11,7 +11,7 @@ "2026-01-01T08:25:00+00:00", "2026-01-01T08:45:00+00:00" ], - "interpolation": "Stepwise", + "interpolation": "Step", "lower_inc": true, "upper_inc": true, "values": [ diff --git a/samples/temporal-properties.json b/samples/temporal-properties.json index ac79cbb..42d29cc 100644 --- a/samples/temporal-properties.json +++ b/samples/temporal-properties.json @@ -55,6 +55,6 @@ "type": "TText" } ], - "timeStamp": "2026-08-29T17:08:43Z" + "timeStamp": "2026-08-29T17:29:23Z" } diff --git a/samples/temporal-property.json b/samples/temporal-property.json index 1b98903..69776a3 100644 --- a/samples/temporal-property.json +++ b/samples/temporal-property.json @@ -8,7 +8,7 @@ "2026-01-01T08:00:00+00:00", "2026-01-01T08:45:00+00:00" ], - "interpolation": "Stepwise", + "interpolation": "Step", "lower_inc": true, "upper_inc": true, "values": [