diff --git a/ats_schema_test.go b/ats_schema_test.go new file mode 100644 index 0000000..d94c721 --- /dev/null +++ b/ats_schema_test.go @@ -0,0 +1,398 @@ +// Every document a `-success` abstract test reads is validated against the schema +// OGC publishes for it, in `openapi/ogcapi-movingfeatures-1.bundled.json`. +// +// The Annex A prose says what a response must carry; the schema says it in a form a +// machine checks. A suite that only asserts the fields it thought to name passes a +// document missing everything it forgot, so the schema is what makes the conformance +// claim mean something. It runs in the same job as every other test — no network, no +// database — because the bundled document resolves all of its own references. +// +// ⛔ THE SCHEMAS ARE NEVER EDITED TO MAKE A TEST PASS. Where the tier and the schema +// disagree the tier is wrong, except where the standard disagrees with ITSELF, which +// ats_response_test.go records and this file then confirms against the schema. +package main + +import ( + "bytes" + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "os" + "testing" + + "github.com/santhosh-tekuri/jsonschema/v6" +) + +const ogcBundlePath = "openapi/ogcapi-movingfeatures-1.bundled.json" + +// ogcBundleURL is where OGC publishes the document vendored at ogcBundlePath. +const ogcBundleURL = "https://schemas.opengis.net/ogcapi/movingfeatures/part1/1.0/openapi/" + + "ogcapi-movingfeatures-1.bundled.json" + +// nullableRewrites is how many `nullable: true` sites the vendored document carries. +// Asserting the count is what keeps the rewrite from silently becoming a no-op: a +// translation that stops applying makes the validator MORE permissive, which no failing +// test would report. +const nullableRewrites = 16 + +// openAPINullable rewrites OpenAPI 3.0's `nullable: true` into the type union a JSON +// Schema validator reads, and counts what it rewrote. Nothing else about the document +// is touched — see openapi/README.md for why no other translation is needed. +func openAPINullable(n any, count *int) any { + switch v := n.(type) { + case map[string]any: + out := make(map[string]any, len(v)) + for k, sub := range v { + out[k] = openAPINullable(sub, count) + } + if nullable, ok := out["nullable"].(bool); ok { + delete(out, "nullable") + if nullable { + *count++ + switch t := out["type"].(type) { + case string: + out["type"] = []any{t, "null"} + case []any: + out["type"] = append(t, "null") + } + } + } + return out + case []any: + out := make([]any, len(v)) + for i, sub := range v { + out[i] = openAPINullable(sub, count) + } + return out + } + return n +} + +// ogcSchemas compiles the vendored document once and answers a validator per schema name. +func ogcSchemas(t *testing.T) func(string) *jsonschema.Schema { + t.Helper() + f, err := os.Open(ogcBundlePath) + if err != nil { + t.Fatalf("the vendored OGC schemas are missing: %v", err) + } + defer f.Close() + doc, err := jsonschema.UnmarshalJSON(f) + if err != nil { + t.Fatalf("reading %s: %v", ogcBundlePath, err) + } + var rewritten int + doc = openAPINullable(doc, &rewritten) + if rewritten != nullableRewrites { + t.Fatalf("the nullable rewrite fired %d times, want %d: the vendored document moved, "+ + "so re-read openapi/README.md before changing this count", rewritten, nullableRewrites) + } + + c := jsonschema.NewCompiler() + // `format` is an annotation by default in JSON Schema, and asserting it is what the + // standard's own motionCurve schema needs to admit the five interpolation values it + // itself names — TestATSSchemaMotionCurveIsInverted measures both readings. + c.AssertFormat() + if err := c.AddResource("ogc.json", doc); err != nil { + t.Fatalf("loading the OGC document: %v", err) + } + return func(name string) *jsonschema.Schema { + s, err := c.Compile("ogc.json#/components/schemas/" + name) + if err != nil { + t.Fatalf("no schema %q in the OGC document: %v", name, err) + } + return s + } +} + +// validate asserts the recorded response is the schema's kind of document. +func validate(t *testing.T, schema *jsonschema.Schema, rec *httptest.ResponseRecorder, what string) { + t.Helper() + if rec.Code != 200 { + t.Fatalf("%s: status = %d, want 200 (%s)", what, rec.Code, rec.Body.String()) + } + doc, err := jsonschema.UnmarshalJSON(bytes.NewReader(rec.Body.Bytes())) + if err != nil { + t.Fatalf("%s: the response is not JSON: %v", what, err) + } + if err := schema.Validate(doc); err != nil { + t.Errorf("%s does not satisfy its OGC schema:\n%v", what, err) + } +} + +func req(method, path string, values map[string]string) *http.Request { + r := httptest.NewRequest(method, path, nil) + for k, v := range values { + r.SetPathValue(k, v) + } + return r +} + +// The documents that need no backend at all. +func TestATSSchemaServiceDocuments(t *testing.T) { + schema := ogcSchemas(t) + for _, c := range []struct { + what string + name string + path string + handler http.HandlerFunc + }{ + {"the landing page", "landingPage", "/", landing}, + {"the conformance declaration", "confClasses", "/conformance", conformance}, + } { + t.Run(c.name, func(t *testing.T) { + rec := httptest.NewRecorder() + c.handler(rec, httptest.NewRequest("GET", c.path, nil)) + validate(t, schema(c.name), rec, c.what) + }) + } +} + +// /conf/mf-collection/collections-get-success and collection-get-success, asserted +// against the schemas rather than against the fields this suite happened to name. +func TestATSSchemaCollectionDocuments(t *testing.T) { + schema := ogcSchemas(t) + withBackend(atsCollectionsBackend(), func() { + rec := httptest.NewRecorder() + listCollections(rec, httptest.NewRequest("GET", "/collections", nil)) + validate(t, schema("collections"), rec, "the Collections document") + + rec = httptest.NewRecorder() + getCollection(rec, req("GET", "/collections/ships", map[string]string{"cid": "ships"})) + validate(t, schema("collection"), rec, "the Collection document") + }) +} + +// /conf/movingfeatures/tproperties-get-success. +func TestATSSchemaTemporalPropertiesDocument(t *testing.T) { + schema := ogcSchemas(t) + f := atsCollectionsBackend() + f.answers = append(f.answers, + fakeAnswer{match: "SELECT 1 FROM", rows: [][]any{{1}}}, + fakeAnswer{match: "to_regclass('mf_tproperty')", rows: [][]any{{"mf_tproperty"}}}, + fakeAnswer{match: "FROM mf_tproperty WHERE", rows: [][]any{ + {"speed", "TReal", uomURI + "km_h-1", "speed over ground"}, + {"label", "TText", "", "vessel label"}, + }}, + ) + withBackend(f, func() { + rec := httptest.NewRecorder() + listTProperties(rec, req("GET", "/collections/ships/items/1/tproperties", + map[string]string{"cid": "ships", "fid": "1"})) + validate(t, schema("temporalProperties"), rec, "the TemporalProperties document") + }) +} + +// /conf/movingfeatures/tgsequence-get-success — and the schema is what settles the +// contradiction TestATSTGSequenceTypeContradiction records: `type` is the enum's single +// value, so a document satisfying Annex A's prose would fail here. +func TestATSSchemaTemporalGeometrySequenceDocument(t *testing.T) { + const mfjson = `{"type":"MovingPoint","crs":{"type":"Name","properties":{"name":"urn:ogc:def:crs:EPSG::4326"}},` + + `"coordinates":[[4.35,50.85],[4.36,50.86]],"datetimes":["2026-01-01T00:00:00+00","2026-01-01T00:10:00+00"],` + + `"interpolation":"Linear"}` + schema := ogcSchemas(t) + f := atsCollectionsBackend() + f.answers = append(f.answers, + fakeAnswer{match: "SELECT 1 FROM", rows: [][]any{{1}}}, + fakeAnswer{match: "SELECT numSequences(", rows: [][]any{{1}}}, + fakeAnswer{match: "SELECT asMFJSON(", rows: [][]any{{mfjson}}}, + ) + withBackend(f, func() { + rec := httptest.NewRecorder() + tgSequence(rec, req("GET", "/collections/ships/items/1/tgsequence", + map[string]string{"cid": "ships", "fid": "1"})) + validate(t, schema("temporalGeometrySequence"), rec, "the TemporalGeometrySequence document") + }) +} + +// The schema is the authority the tier follows where Annex A's prose contradicts it, so +// the enum it declares is asserted directly: a reader of this suite finds the standard's +// own artifact rather than a constant somebody chose. +func TestATSSchemaDeclaresTGSequenceType(t *testing.T) { + f, err := os.Open(ogcBundlePath) + if err != nil { + t.Fatal(err) + } + defer f.Close() + var doc struct { + Components struct { + Schemas map[string]struct { + Required []string `json:"required"` + Properties map[string]struct { + Enum []string `json:"enum"` + } `json:"properties"` + } `json:"schemas"` + } `json:"components"` + } + if err := json.NewDecoder(f).Decode(&doc); err != nil { + t.Fatal(err) + } + s, ok := doc.Components.Schemas["temporalGeometrySequence"] + if !ok { + t.Fatal("the OGC document declares no temporalGeometrySequence schema") + } + if got := s.Properties["type"].Enum; len(got) != 1 || got[0] != "TemporalGeometrySequence" { + t.Errorf("type enum = %v, want [TemporalGeometrySequence] — the schema moved, and the "+ + "contradiction ats_response_test.go records needs re-reading", got) + } + var hasSequence bool + for _, r := range s.Required { + if r == "geometrySequence" { + hasSequence = true + } + } + if !hasSequence { + t.Errorf("required = %v, want geometrySequence among them", s.Required) + } +} + +// ⛔ THE motionCurve SCHEMA IS INVERTED UNDER JSON SCHEMA'S OWN DEFAULT, and this test +// is the measurement rather than an argument. /components/schemas/motionCurve is +// +// oneOf: [ {string, enum: [Discrete, Step, Linear, Quadratic, Cubic]}, +// {string, format: uri} ] +// +// `oneOf` admits a value matching EXACTLY ONE branch. JSON Schema makes `format` an +// annotation unless a validator opts in, so branch 1 reduces to "any string": every one +// of the five interpolations the standard names matches BOTH branches and is rejected, +// while an arbitrary string matches branch 1 alone and is accepted. A validator run at +// the specification's default settings therefore rejects exactly the values the standard +// defines and accepts the ones it does not. +// +// The fix this measures — keeping `oneOf` and giving the URI branch an absolute-URI +// pattern, so the two branches stop overlapping — answers correctly whichever way a +// validator treats `format`. It is offered to OGC; the schema itself is not edited here. +func TestATSSchemaMotionCurveIsInverted(t *testing.T) { + const published = `{"oneOf":[ + {"type":"string","enum":["Discrete","Step","Linear","Quadratic","Cubic"],"default":"Linear"}, + {"type":"string","format":"uri"}]}` + const proposed = `{"oneOf":[ + {"type":"string","enum":["Discrete","Step","Linear","Quadratic","Cubic"],"default":"Linear"}, + {"type":"string","format":"uri","pattern":"^[A-Za-z][A-Za-z0-9+.-]*:"}]}` + + compile := func(src string, assertFormat bool) *jsonschema.Schema { + d, err := jsonschema.UnmarshalJSON(bytes.NewReader([]byte(src))) + if err != nil { + t.Fatal(err) + } + c := jsonschema.NewCompiler() + if assertFormat { + c.AssertFormat() + } + if err := c.AddResource("m.json", d); err != nil { + t.Fatal(err) + } + s, err := c.Compile("m.json") + if err != nil { + t.Fatal(err) + } + return s + } + accepts := func(s *jsonschema.Schema, v string) bool { return s.Validate(any(v)) == nil } + + named := []string{"Discrete", "Step", "Linear", "Quadratic", "Cubic"} + const aURI, notACurve = "http://example.org/curve", "Bogus" + + // The finding: at the specification's default the published schema is inverted. + pub := compile(published, false) + for _, v := range named { + if accepts(pub, v) { + t.Errorf("the published motionCurve accepts %q at default format handling; "+ + "this test records that it does not, so the schema has been corrected "+ + "and openapi/README.md needs re-reading", v) + } + } + if !accepts(pub, notACurve) { + t.Errorf("the published motionCurve rejects %q at default format handling; "+ + "this test records that it accepts it", notACurve) + } + + // The fix answers the same under both readings, which is what makes it a fix rather + // than a second way to be right by accident. + for _, assert := range []bool{false, true} { + s := compile(proposed, assert) + for _, v := range append(append([]string{}, named...), aURI) { + if !accepts(s, v) { + t.Errorf("the proposed motionCurve rejects %q (assertFormat=%v)", v, assert) + } + } + if accepts(s, notACurve) { + t.Errorf("the proposed motionCurve accepts %q (assertFormat=%v)", notACurve, assert) + } + } +} + +// ⛔ WHAT THIS VALIDATION CANNOT SEE, STATED RATHER THAN LEFT IMPLICIT. A temporal +// GEOMETRY's datetimes are `{"type": "string"}` with no format, while a temporal +// PROPERTY's are `{"type": "string", "format": "date-time"}`. The same concept carries +// two different constraints, so validating a TemporalGeometrySequence says nothing +// about the shape of the instants inside it — any string passes. +// +// That matters because the standard's own normative text is not silent on the point: +// it says the syntax of a date-time is RFC 3339 section 5.6, and that a server SHALL +// interpret it so. The geometry schema under-constrains against its own standard. +// +// This test pins the asymmetry so a reader of the suite finds the gap rather than +// mistaking a passing document for a checked one, and so a corrected schema surfaces +// here as a failure to simplify. +func TestATSSchemaDatetimeFormatIsAsymmetric(t *testing.T) { + f, err := os.Open(ogcBundlePath) + if err != nil { + t.Fatal(err) + } + defer f.Close() + var doc struct { + Components struct { + Schemas map[string]struct { + Properties map[string]struct { + Items struct { + Type string `json:"type"` + Format string `json:"format"` + } `json:"items"` + } `json:"properties"` + } `json:"schemas"` + } `json:"components"` + } + if err := json.NewDecoder(f).Decode(&doc); err != nil { + t.Fatal(err) + } + geom := doc.Components.Schemas["temporalPrimitiveGeometry"].Properties["datetimes"].Items + value := doc.Components.Schemas["temporalPrimitiveValue"].Properties["datetimes"].Items + if value.Format != "date-time" { + t.Errorf("temporalPrimitiveValue.datetimes items format = %q, want date-time", value.Format) + } + if geom.Format != "" { + t.Errorf("temporalPrimitiveGeometry.datetimes items now carry format %q — the asymmetry "+ + "this test records is gone, so the note above it and openapi/README.md need re-reading", + geom.Format) + } + if geom.Type != "string" { + t.Errorf("temporalPrimitiveGeometry.datetimes items type = %q, want string", geom.Type) + } +} + +// The vendored copy is what OGC publishes. It needs the network, so it runs only when +// asked for; openapi/README.md carries the URL and the checksum it is pinned at. +func TestATSSchemaBundleMatchesOGC(t *testing.T) { + if os.Getenv("MFAPI_SCHEMA_FRESHNESS") == "" { + t.Skip("set MFAPI_SCHEMA_FRESHNESS=1 to fetch " + ogcBundleURL + " and compare") + } + resp, err := http.Get(ogcBundleURL) + if err != nil { + t.Fatalf("fetching the published schemas: %v", err) + } + defer resp.Body.Close() + published, err := io.ReadAll(resp.Body) + if err != nil { + t.Fatal(err) + } + vendored, err := os.ReadFile(ogcBundlePath) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(vendored, published) { + t.Errorf("%s differs from %s: %d bytes vendored, %d published. Re-vendor it unchanged "+ + "and re-read what moved; never edit the copy.", + ogcBundlePath, ogcBundleURL, len(vendored), len(published)) + } +} diff --git a/go.mod b/go.mod index 0332ff9..3ba99e3 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/marcboeker/go-duckdb/v2 v2.4.3 github.com/mochi-mqtt/server/v2 v2.7.9 github.com/parquet-go/parquet-go v0.30.1 + github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 ) require ( diff --git a/go.sum b/go.sum index e07b287..9448bc1 100644 --- a/go.sum +++ b/go.sum @@ -17,6 +17,8 @@ github.com/apache/thrift v0.22.0/go.mod h1:1e7J/O1Ae6ZQMTYdy9xa3w9k+XHWPfRvdPyJe github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/duckdb/duckdb-go-bindings v0.1.21 h1:bOb/MXNT4PN5JBZ7wpNg6hrj9+cuDjWDa4ee9UdbVyI= github.com/duckdb/duckdb-go-bindings v0.1.21/go.mod h1:pBnfviMzANT/9hi4bg+zW4ykRZZPCXlVuvBWEcZofkc= github.com/duckdb/duckdb-go-bindings/darwin-amd64 v0.1.21 h1:Sjjhf2F/zCjPF53c2VXOSKk0PzieMriSoyr5wfvr9d8= @@ -102,6 +104,8 @@ github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZV github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 h1:1EYB5IzjZawrrnELUi78f9fPu57HuXjmddZPjrls/28= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.3/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= diff --git a/openapi/README.md b/openapi/README.md new file mode 100644 index 0000000..12373fb --- /dev/null +++ b/openapi/README.md @@ -0,0 +1,58 @@ +# The normative OGC API — Moving Features Part 1 schemas + +`ogcapi-movingfeatures-1.bundled.json` is the OpenAPI 3.0.3 document OGC publishes for +*OGC API — Moving Features — Part 1: Core* (OGC 22-003r3), vendored byte for byte: + + URL https://schemas.opengis.net/ogcapi/movingfeatures/part1/1.0/openapi/ogcapi-movingfeatures-1.bundled.json + bytes 108910 + sha256 7e8e0a0e68c936dd59ca40ed50b63eb3121ef96082307438e54bb01362276866 + +It is the bundled form, so every `$ref` resolves inside the one file and validation needs no +network. `components.schemas` holds the 36 schemas the standard defines, and +`ats_schema_test.go` validates the documents this tier emits against them. + +The copy is never edited. To confirm it still matches what OGC publishes: + + MFAPI_SCHEMA_FRESHNESS=1 go test -run TestATSSchemaBundleMatchesOGC -v . + +That test fetches the URL above and compares; it skips without the variable so the suite +stays offline. + +## Why the bundle rather than the individual schema files + +The same directory publishes `schemas/*.yaml` one file per schema, each `$ref`-ing its +siblings by relative path. Reading those needs a resolver that walks the directory and a +YAML parser; the bundle carries identical content with the references already resolved, and +is what a validator can load directly. + +## The one translation the validator applies + +OpenAPI 3.0 spells nullability as `nullable: true` beside a `type`, where a JSON Schema +draft spells the same thing as a type union. A JSON Schema validator does not know that keyword +and would silently ignore it, rejecting a null the standard admits. The validator therefore +rewrites `{"type": "T", "nullable": true}` to `{"type": ["T", "null"]}` before compiling, +and asserts it made exactly the 16 rewrites this document calls for — a translation that +silently stopped applying would make the run permissive without saying so. + +Nothing else is rewritten. Measured on this document: 0 schemas put a sibling keyword beside +a `$ref` (where OpenAPI ignores the sibling and JSON Schema 2020-12 applies it), and neither +`exclusiveMinimum` nor `exclusiveMaximum` appears in the boolean form OpenAPI uses, so the +two dialects agree on every other keyword present. + +`format` is asserted rather than treated as an annotation. JSON Schema leaves that to the +validator, and the assertion is what the standard's own `motionCurve` needs to admit the five +interpolation values it names: at the default reading its two `oneOf` branches both match a +plain string, so every named value matches both and is rejected while an arbitrary string +matches one and passes. `TestATSSchemaMotionCurveIsInverted` measures both readings and the +correction proposed for them. + +## What this validation does not cover + +A temporal GEOMETRY's `datetimes` are `{"type": "string"}` with no format, where a temporal +PROPERTY's are `{"type": "string", "format": "date-time"}`. Validating a +TemporalGeometrySequence therefore says nothing about the shape of the instants inside it — +any string passes. The standard is not silent on the point in its prose: it states that the +syntax of a date-time is RFC 3339 section 5.6 and that a server SHALL interpret it so, which +makes the geometry schema weaker than the standard it belongs to. +`TestATSSchemaDatetimeFormatIsAsymmetric` pins the asymmetry, so the gap is stated rather than +mistaken for coverage. diff --git a/openapi/ogcapi-movingfeatures-1.bundled.json b/openapi/ogcapi-movingfeatures-1.bundled.json new file mode 100644 index 0000000..f756226 --- /dev/null +++ b/openapi/ogcapi-movingfeatures-1.bundled.json @@ -0,0 +1,3392 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Building Blocks specified in OGC API - Moving Features - Part 1: Core", + "version": "1.0.0", + "description": "This is the OpenAPI definition of the OGC API - Moving Features - Part 1: Core Standard. The API also implements the OGC Moving Features Encoding Extension - JSON Standard.", + "contact": { + "name": "Open Geospatial Consortium", + "email": "info@ogc.org" + }, + "license": { + "name": "OGC License", + "url": "https://www.ogc.org/legal/" + }, + "x-logo": { + "url": "https://www.ogc.org/pub/www/files/OGC_Logo_2D_Blue_x_0_0.png" + } + }, + "tags": [ + { + "name": "Capabilities", + "description": "Essential characteristics of the information available from the API." + }, + { + "name": "MovingFeatureCollection", + "description": "Collections of moving features to be logically managed by a user." + }, + { + "name": "MovingFeatures", + "description": "Moving feature data, including the temporal geometry, temporal properties, etc." + }, + { + "name": "TemporalGeometry", + "description": "The spatial change over time (temporal geometry), representing the movement of the rigid or nonrigid body of a feature." + }, + { + "name": "TemporalGeometryQuery", + "description": "Queryable resources for the temporal primitive geometry." + }, + { + "name": "TemporalProperty", + "description": "The thematic change over time (temporal property), representing the variation of the value of any descriptive characteristic of a feature." + } + ], + "paths": { + "/": { + "get": { + "operationId": "getLandingPage", + "summary": "Landing page", + "description": "The landing page provides links to the API definition, the conformance statements and to the feature collections in this dataset.", + "tags": [ + "Capabilities" + ], + "responses": { + "200": { + "$ref": "#/components/responses/LandingPage" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/conformance": { + "get": { + "operationId": "getConformance", + "summary": "Information about specifications that this API conforms to", + "description": "A list of all conformance classes specified in a standard that the server conforms to.", + "tags": [ + "Capabilities" + ], + "responses": { + "200": { + "$ref": "#/components/responses/Conformance" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/api": { + "get": { + "operationId": "getAPIList", + "summary": "API definition", + "description": "A list of all API definition", + "tags": [ + "Capabilities" + ], + "responses": { + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections": { + "get": { + "operationId": "searchCatalog", + "summary": "Retrieve catalogs of moving features collection", + "description": "A user can retrieve catalogs to access collections by simple filtering and a limit.\n", + "tags": [ + "Capabilities" + ], + "responses": { + "200": { + "$ref": "#/components/responses/Collections" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + }, + "post": { + "operationId": "registerMetadata", + "summary": "Register metadata about a collection of moving features", + "description": "A user SHOULD register metadata about a collection of moving features into the system.\n", + "tags": [ + "MovingFeatureCollection" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/collection-body" + }, + "example": { + "title": "moving_feature_collection_sample", + "updateFrequency": 1000, + "description": "example", + "itemType": "movingfeature" + } + } + } + }, + "responses": { + "201": { + "description": "Successful create a collection to manage moving features.", + "headers": { + "Location": { + "description": "A URI of the newly added resource", + "schema": { + "type": "string", + "example": "https://data.example.org/collections/mfc-1" + } + } + } + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}": { + "get": { + "operationId": "accessMetadata", + "summary": "Access metadata about the collection", + "description": "A user can access metadata with id `collectionId`.\n", + "tags": [ + "MovingFeatureCollection" + ], + "parameters": [ + { + "$ref": "#/components/parameters/collectionId" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Collection" + }, + "404": { + "description": "A collection with the specified id was not found." + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + }, + "delete": { + "operationId": "deleteCollection", + "summary": "Delete the collection", + "description": "The collection catalog with id `collectionId` and including metadata and moving features SHOULD be deleted.\n", + "tags": [ + "MovingFeatureCollection" + ], + "parameters": [ + { + "$ref": "#/components/parameters/collectionId" + } + ], + "responses": { + "204": { + "description": "Successfully deleted." + }, + "404": { + "description": "A collection with the specified name was not found." + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + }, + "put": { + "operationId": "replaceMetadata", + "summary": "Replace metadata about the collection", + "description": "A user SHOULD replace metadata with id `collectionId`.\n\nThe request body schema is the same the POST's one. \n\nHowever, `updateFrequency` property is NOT updated.\n", + "tags": [ + "MovingFeatureCollection" + ], + "parameters": [ + { + "$ref": "#/components/parameters/collectionId" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/collection-body" + }, + "example": { + "title": "moving_feature_collection_sample", + "updateFrequency": 1000, + "description": "example", + "itemType": "movingfeature" + } + } + } + }, + "responses": { + "204": { + "description": "Successfully replaced." + }, + "404": { + "description": "A collection with the specified name was not found." + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/items": { + "get": { + "operationId": "retrieveMovingFeatures", + "summary": "Retrieve moving feature collection", + "description": "A user can retrieve moving feature collection to access the static information of the moving feature by simple filtering and a limit.\n\nSpecifically, if the `subTrajectory` parameter is \"true\", it will return the temporal geometry within the time interval specified by `datetime` parameter.\n", + "tags": [ + "MovingFeatures" + ], + "parameters": [ + { + "$ref": "#/components/parameters/collectionId" + }, + { + "$ref": "#/components/parameters/bbox" + }, + { + "$ref": "#/components/parameters/datetime" + }, + { + "$ref": "#/components/parameters/limit" + }, + { + "$ref": "#/components/parameters/subtrajectory" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/MovingFeatures" + }, + "404": { + "description": "A collection with the specified id was not found." + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + }, + "post": { + "operationId": "insertMovingFeatures", + "summary": "Insert moving features", + "description": "A user SHOULD insert a set of moving features or a moving feature into a collection with id `collectionId`.\n\nThe request body schema SHALL follows the [MovingFeature object](https://docs.opengeospatial.org/is/19-045r3/19-045r3.html#mfeature) or \n[MovingFeatureCollection object](https://docs.opengeospatial.org/is/19-045r3/19-045r3.html#mfeaturecollection) in the OGC MF-JSON.\n", + "tags": [ + "MovingFeatures" + ], + "parameters": [ + { + "$ref": "#/components/parameters/collectionId" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/movingFeature-mfjson" + }, + { + "$ref": "#/components/schemas/movingFeatureCollection" + } + ] + }, + "example": { + "type": "Feature", + "crs": { + "type": "Name", + "properties": { + "name": "urn:ogc:def:crs:OGC:1.3:CRS84" + } + }, + "trs": { + "type": "Link", + "properties": { + "type": "OGCDEF", + "href": "http://www.opengis.net/def/uom/ISO-8601/0/Gregorian" + } + }, + "temporalGeometry": { + "type": "MovingPoint", + "datetimes": [ + "2011-07-14T22:01:01Z", + "2011-07-14T22:01:02Z", + "2011-07-14T22:01:03Z", + "2011-07-14T22:01:04Z", + "2011-07-14T22:01:05Z" + ], + "coordinates": [ + [ + 139.757083, + 35.627701, + 0.5 + ], + [ + 139.757399, + 35.627701, + 2 + ], + [ + 139.757555, + 35.627688, + 4 + ], + [ + 139.757651, + 35.627596, + 4 + ], + [ + 139.757716, + 35.627483, + 4 + ] + ], + "interpolation": "Linear", + "base": { + "type": "glTF", + "href": "http://www.opengis.net/spec/movingfeatures/json/1.0/prism/example/car3dmodel.gltf" + }, + "orientations": [ + { + "scales": [ + 1, + 1, + 1 + ], + "angles": [ + 0, + 0, + 0 + ] + }, + { + "scales": [ + 1, + 1, + 1 + ], + "angles": [ + 0, + 355, + 0 + ] + }, + { + "scales": [ + 1, + 1, + 1 + ], + "angles": [ + 0, + 0, + 330 + ] + }, + { + "scales": [ + 1, + 1, + 1 + ], + "angles": [ + 0, + 0, + 300 + ] + }, + { + "scales": [ + 1, + 1, + 1 + ], + "angles": [ + 0, + 0, + 270 + ] + } + ] + }, + "temporalProperties": [ + { + "datetimes": [ + "2011-07-14T22:01:01.450Z", + "2011-07-14T23:01:01.450Z", + "2011-07-15T00:01:01.450Z" + ], + "length": { + "type": "Measure", + "form": "http://qudt.org/vocab/quantitykind/Length", + "values": [ + 1, + 2.4, + 1 + ], + "interpolation": "Linear" + }, + "discharge": { + "type": "Measure", + "form": "MQS", + "values": [ + 3, + 4, + 5 + ], + "interpolation": "Step" + } + }, + { + "datetimes": [ + 1465621816590, + 1465711526300 + ], + "camera": { + "type": "Image", + "values": [ + "http://www.opengis.net/spec/movingfeatures/json/1.0/prism/example/image1", + "iVBORw0KGgoAAAANSUhEU......" + ], + "interpolation": "Discrete" + }, + "labels": { + "type": "Text", + "values": [ + "car", + "human" + ], + "interpolation": "Discrete" + } + } + ], + "geometry": { + "type": "LineString", + "coordinates": [ + [ + 139.757083, + 35.627701, + 0.5 + ], + [ + 139.757399, + 35.627701, + 2 + ], + [ + 139.757555, + 35.627688, + 4 + ], + [ + 139.757651, + 35.627596, + 4 + ], + [ + 139.757716, + 35.627483, + 4 + ] + ] + }, + "properties": { + "name": "car1", + "state": "test1", + "video": "http://www.opengis.net/spec/movingfeatures/json/1.0/prism/example/video.mpeg" + }, + "bbox": [ + 139.757083, + 35.627483, + 0, + 139.757716, + 35.627701, + 4.5 + ], + "time": [ + "2011-07-14T22:01:01Z", + "2011-07-15T01:11:22Z" + ], + "id": "mf-1" + } + } + } + }, + "responses": { + "201": { + "description": "Successful create a set of moving features or a moving feature into a specific collection.\n", + "headers": { + "Locations": { + "description": "A list of URI of the newly added resources", + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "https://data.example.org/collections/mfc-1/items/mf-1", + "https://data.example.org/collections/mfc-1/items/109301273" + ] + } + } + } + }, + "400": { + "description": "A query parameter was not validly used." + }, + "404": { + "description": "A collection with the specified id was not found." + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/items/{mFeatureId}": { + "get": { + "operationId": "accessMovingFeature", + "summary": "Access the static data of the moving feature", + "description": "A user can access a static data of a moving feature with id `mFeatureId`.\n\nThe static data of a moving feature is not included temporal geometries and temporal properties.\n", + "tags": [ + "MovingFeatures" + ], + "parameters": [ + { + "$ref": "#/components/parameters/collectionId" + }, + { + "$ref": "#/components/parameters/mFeatureId" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/MovingFeature" + }, + "404": { + "description": "- A collection with the specified id was not found.\n- Or a moving feature with the specified id was not found.\n" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + }, + "delete": { + "operationId": "deleteMovingFeature", + "summary": "Delete a single moving feature", + "description": "The moving feature with id `mFeatureId` and including temporal geometries and properties SHOULD be deleted.\n", + "tags": [ + "MovingFeatures" + ], + "parameters": [ + { + "$ref": "#/components/parameters/collectionId" + }, + { + "$ref": "#/components/parameters/mFeatureId" + } + ], + "responses": { + "204": { + "description": "Successfully deleted." + }, + "404": { + "description": "- A collection with the specified id was not found.\n- Or a moving feature with the specified id was not found.\n" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/items/{mFeatureId}/tgsequence": { + "get": { + "operationId": "retrieveTemporalGeometrySequence", + "summary": "Retrieve the movement data of the single moving feature", + "description": "A user can retrieve only the movement data of a moving feature with id `mFeatureId` by simple filtering and a limit.\n", + "tags": [ + "TemporalGeometry" + ], + "parameters": [ + { + "$ref": "#/components/parameters/collectionId" + }, + { + "$ref": "#/components/parameters/mFeatureId" + }, + { + "$ref": "#/components/parameters/bbox" + }, + { + "$ref": "#/components/parameters/datetime" + }, + { + "$ref": "#/components/parameters/limit" + }, + { + "$ref": "#/components/parameters/leaf" + }, + { + "$ref": "#/components/parameters/subtrajectory" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TemporalGeometrySequence" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + }, + "post": { + "operationId": "insertTemporalPrimitiveGeometry", + "summary": "Add movement data into the moving feature", + "description": "A user SHOULD add more movement data into a moving feature with id `mFeatureId`.\n\nThe request body schema SHALL follows the [TemporalPrimitiveGeometry object](https://docs.ogc.org/is/19-045r3/19-045r3.html#tprimitive) in the OGC MF-JSON.\n", + "tags": [ + "TemporalGeometry" + ], + "parameters": [ + { + "$ref": "#/components/parameters/collectionId" + }, + { + "$ref": "#/components/parameters/mFeatureId" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/temporalPrimitiveGeometry" + }, + "example": { + "type": "MovingPoint", + "datetimes": [ + "2011-07-14T22:01:06Z", + "2011-07-14T22:01:07Z", + "2011-07-14T22:01:08Z", + "2011-07-14T22:01:09Z", + "2011-07-14T22:01:10Z" + ], + "coordinates": [ + [ + 139.757083, + 35.627701, + 0.5 + ], + [ + 139.757399, + 35.627701, + 2 + ], + [ + 139.757555, + 35.627688, + 4 + ], + [ + 139.757651, + 35.627596, + 4 + ], + [ + 139.757716, + 35.627483, + 4 + ] + ], + "interpolation": "Linear", + "base": { + "type": "glTF", + "href": "https://www.opengis.net/spec/movingfeatures/json/1.0/prism/example/car3dmodel.gltf" + }, + "orientations": [ + { + "scales": [ + 1, + 1, + 1 + ], + "angles": [ + 0, + 0, + 0 + ] + }, + { + "scales": [ + 1, + 1, + 1 + ], + "angles": [ + 0, + 355, + 0 + ] + }, + { + "scales": [ + 1, + 1, + 1 + ], + "angles": [ + 0, + 0, + 330 + ] + }, + { + "scales": [ + 1, + 1, + 1 + ], + "angles": [ + 0, + 0, + 300 + ] + }, + { + "scales": [ + 1, + 1, + 1 + ], + "angles": [ + 0, + 0, + 270 + ] + } + ] + } + } + } + }, + "responses": { + "201": { + "description": "Successful add more movement data into a specified moving feature.\n", + "headers": { + "Location": { + "description": "A URI of the newly added resource", + "schema": { + "type": "string", + "example": "https://data.example.org/collections/mfc-1/items/mf-1/tgsequence/tg-2" + } + } + } + }, + "400": { + "description": "A query parameter was not validly used." + }, + "404": { + "description": "- A collection with the specified id was not found.\n- Or a moving feature with the specified id was not found.\n" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/items/{mFeatureId}/tgsequence/{tGeometryId}": { + "delete": { + "operationId": "deleteTemporalPrimitiveGeometry", + "summary": "Delete a singe temporal primitive geometry", + "description": "The temporal primitive geometry with id `tGeometryId` SHOULD be deleted.\n", + "tags": [ + "TemporalGeometry" + ], + "parameters": [ + { + "$ref": "#/components/parameters/collectionId" + }, + { + "$ref": "#/components/parameters/mFeatureId" + }, + { + "$ref": "#/components/parameters/tGeometryId" + } + ], + "responses": { + "204": { + "description": "Successfully deleted." + }, + "404": { + "description": "- A collection with the specified id was not found.\n- Or a moving feature with the specified id was not found.\n- Or a temporal primitive geometry with the specified id was not found.\n" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/items/{mFeatureId}/tgsequence/{tGeometryId}/distance": { + "get": { + "operationId": "getDistanceOfTemporalPrimitiveGeometry", + "summary": "Get a time-to-distance curve of a temporal primitive geometry", + "description": "A user can get time-to-distance curve of a temporal primitive geometry with id `tGeometryId`.\n", + "tags": [ + "TemporalGeometryQuery" + ], + "parameters": [ + { + "$ref": "#/components/parameters/collectionId" + }, + { + "$ref": "#/components/parameters/mFeatureId" + }, + { + "$ref": "#/components/parameters/tGeometryId" + }, + { + "$ref": "#/components/parameters/datetime" + }, + { + "$ref": "#/components/parameters/leaf" + }, + { + "$ref": "#/components/parameters/subtemporalvalue" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/DistanceQuery" + }, + "400": { + "description": "A query parameter was not validly used." + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/items/{mFeatureId}/tgsequence/{tGeometryId}/velocity": { + "get": { + "operationId": "getVelocityOfTemporalPrimitiveGeometry", + "summary": "Get a time-to-velocity curve of a temporal primitive geometry", + "description": "A user can get time-to-velocity curve of a temporal primitive geometry with id `tGeometryId`.\n", + "tags": [ + "TemporalGeometryQuery" + ], + "parameters": [ + { + "$ref": "#/components/parameters/collectionId" + }, + { + "$ref": "#/components/parameters/mFeatureId" + }, + { + "$ref": "#/components/parameters/tGeometryId" + }, + { + "$ref": "#/components/parameters/datetime" + }, + { + "$ref": "#/components/parameters/leaf" + }, + { + "$ref": "#/components/parameters/subtemporalvalue" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/VelocityQuery" + }, + "400": { + "description": "A query parameter was not validly used." + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/items/{mFeatureId}/tgsequence/{tGeometryId}/acceleration": { + "get": { + "operationId": "getAccelerationOfTemporalPrimitiveGeometry", + "summary": "Get a time-to-acceleration curve of a temporal primitive geometry", + "description": "A user can get time-to-acceleration curve of a temporal primitive geometry with id `tGeometryId`.\n", + "tags": [ + "TemporalGeometryQuery" + ], + "parameters": [ + { + "$ref": "#/components/parameters/collectionId" + }, + { + "$ref": "#/components/parameters/mFeatureId" + }, + { + "$ref": "#/components/parameters/tGeometryId" + }, + { + "$ref": "#/components/parameters/datetime" + }, + { + "$ref": "#/components/parameters/leaf" + }, + { + "$ref": "#/components/parameters/subtemporalvalue" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/AccelerationQuery" + }, + "400": { + "description": "A query parameter was not validly used." + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/items/{mFeatureId}/tproperties": { + "get": { + "operationId": "retrieveTemporalProperties", + "summary": "Retrieve a set of the temporal property data", + "description": "A user can retrieve the static information of the temporal property data that included a single moving feature with id `mFeatureId`.\n\nThe static data of a temporal property is not included temporal values (property `valueSequence`).\n\nAlso a user can retrieve the sub sequence of the temporal information of the temporal property data for the specified time interval with `subTemporalValue` query parameter. \nIn this case, `temporalProperties` property schema SHALL follows the [TemporalProperties object](https://docs.ogc.org/is/19-045r3/19-045r3.html#tproperties) in the OGC MF-JSON.\n", + "tags": [ + "TemporalProperty" + ], + "parameters": [ + { + "$ref": "#/components/parameters/collectionId" + }, + { + "$ref": "#/components/parameters/mFeatureId" + }, + { + "$ref": "#/components/parameters/datetime" + }, + { + "$ref": "#/components/parameters/limit" + }, + { + "$ref": "#/components/parameters/subtemporalvalue" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TemporalProperties" + }, + "400": { + "description": "A query parameter was not validly used." + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + }, + "post": { + "operationId": "insertTemporalProperty", + "summary": "Add temporal property data", + "description": "A user SHOULD add new temporal property data into a moving feature with id `mFeatureId`.\n\nThe request body schema SHALL follows the [TemporalProperties object](https://docs.opengeospatial.org/is/19-045r3/19-045r3.html#tproperties) in the OGC MF-JSON.\n", + "tags": [ + "TemporalProperty" + ], + "parameters": [ + { + "$ref": "#/components/parameters/collectionId" + }, + { + "$ref": "#/components/parameters/mFeatureId" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/temporalProperties-mfjson" + }, + "example": [ + { + "datetimes": [ + "2011-07-14T22:01:01.450Z", + "2011-07-14T23:01:01.450Z", + "2011-07-15T00:01:01.450Z" + ], + "length": { + "type": "Measure", + "form": "http://qudt.org/vocab/quantitykind/Length", + "values": [ + 1, + 2.4, + 1 + ], + "interpolation": "Linear" + }, + "discharge": { + "type": "Measure", + "form": "MQS", + "values": [ + 3, + 4, + 5 + ], + "interpolation": "Step" + } + }, + { + "datetimes": [ + "2011-07-14T22:01:01.450Z", + "2011-07-14T23:01:01.450Z" + ], + "camera": { + "type": "Image", + "values": [ + "http://www.opengis.net/spec/movingfeatures/json/1.0/prism/example/image1", + "iVBORw0KGgoAAAANSUhEU......" + ], + "interpolation": "Discrete" + }, + "labels": { + "type": "Text", + "values": [ + "car", + "human" + ], + "interpolation": "Discrete" + } + } + ] + } + } + }, + "responses": { + "201": { + "description": "Successful add more temporal property into a specified moving feature.\n", + "headers": { + "Locations": { + "description": "A list of URI of the newly added resources", + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "https://data.example.org/collections/mfc-1/items/mf-1/tproperties/length", + "https://data.example.org/collections/mfc-1/items/mf-1/tproperties/discharge", + "https://data.example.org/collections/mfc-1/items/mf-1/tproperties/camera", + "https://data.example.org/collections/mfc-1/items/mf-1/tproperties/labels" + ] + } + } + } + }, + "404": { + "description": "- A collection with the specified id was not found.\n- Or a moving feature with the specified id was not found.\n" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/items/{mFeatureId}/tproperties/{tPropertyName}": { + "get": { + "operationId": "retrieveTemporalProperty", + "summary": "Retrieve a temporal property", + "description": "A user can retrieve only the temporal values with a specified name `tPropertyName` of temporal property.\n", + "tags": [ + "TemporalProperty" + ], + "parameters": [ + { + "$ref": "#/components/parameters/collectionId" + }, + { + "$ref": "#/components/parameters/mFeatureId" + }, + { + "$ref": "#/components/parameters/tPropertyName" + }, + { + "$ref": "#/components/parameters/datetime" + }, + { + "$ref": "#/components/parameters/leaf" + }, + { + "$ref": "#/components/parameters/subtemporalvalue" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TemporalProperty" + }, + "400": { + "description": "A query parameter was not validly used." + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + }, + "post": { + "operationId": "insertTemporalPrimitiveValue", + "summary": "Add temporal primitive value data", + "description": "A user SHOULD add more temporal primitive value data into a temporal property with id `tPropertyName`.\n", + "tags": [ + "TemporalProperty" + ], + "parameters": [ + { + "$ref": "#/components/parameters/collectionId" + }, + { + "$ref": "#/components/parameters/mFeatureId" + }, + { + "$ref": "#/components/parameters/tPropertyName" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/temporalPrimitiveValue" + }, + "example": { + "datetimes": [ + "2011-07-15T08:00:00Z", + "2011-07-15T08:00:01Z", + "2011-07-15T08:00:02Z" + ], + "values": [ + 0, + 20, + 50 + ], + "interpolation": "Linear" + } + } + } + }, + "responses": { + "201": { + "description": "Successful add more temporal primitive value data into a specified temporal property.\n", + "headers": { + "Location": { + "description": "A URI of the newly added resource", + "schema": { + "type": "string", + "example": "https://data.example.org/collections/mfc-1/items/mf-1/tproperties/tvalue/tpv-1" + } + } + } + }, + "404": { + "description": "- A collection with the specified id was not found.\n- Or a moving feature with the specified id was not found.\n- Or a temporal property with the specified id was not found.\n" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + }, + "delete": { + "operationId": "deleteTemporalProperty", + "summary": "Delete a specified temporal property", + "description": "The temporal property with id `tPropertyName` SHOULD be deleted.\n", + "tags": [ + "TemporalProperty" + ], + "parameters": [ + { + "$ref": "#/components/parameters/collectionId" + }, + { + "$ref": "#/components/parameters/mFeatureId" + }, + { + "$ref": "#/components/parameters/tPropertyName" + } + ], + "responses": { + "204": { + "description": "Successfully deleted." + }, + "404": { + "description": "- A collection with the specified id was not found.\n- Or a moving feature with the specified id was not found.\n- Or a temporal property with the specified id was not found.\n" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/items/{mFeatureId}/tproperties/{tPropertyName}/{tValueId}": { + "delete": { + "operationId": "deleteTemporalPrimitiveValue", + "summary": "Delete a singe temporal primitive value", + "description": "The temporal primitive value with id `tValueId` SHOULD be deleted.\n", + "tags": [ + "TemporalProperty" + ], + "parameters": [ + { + "$ref": "#/components/parameters/collectionId" + }, + { + "$ref": "#/components/parameters/mFeatureId" + }, + { + "$ref": "#/components/parameters/tPropertyName" + }, + { + "$ref": "#/components/parameters/tValueId" + } + ], + "responses": { + "204": { + "description": "Successfully deleted." + }, + "404": { + "description": "- A collection with the specified id was not found.\n- Or a moving feature with the specified id was not found.\n- Or a temporal property with the specified id was not found.\n- Or a temporal primitive primitive with the specified id was not found.\n" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + } + }, + "components": { + "schemas": { + "link": { + "type": "object", + "required": [ + "href", + "rel" + ], + "properties": { + "href": { + "type": "string", + "example": "http://data.example.com/buildings/123" + }, + "rel": { + "type": "string", + "example": "alternate" + }, + "type": { + "type": "string", + "example": "application/geo+json" + }, + "hreflang": { + "type": "string", + "example": "en" + }, + "title": { + "type": "string", + "example": "Trierer Strasse 70, 53115 Bonn" + }, + "length": { + "type": "integer" + } + } + }, + "landingPage": { + "type": "object", + "required": [ + "links" + ], + "properties": { + "title": { + "type": "string", + "example": "Moving features data server" + }, + "description": { + "type": "string", + "example": "Access to data about moving features" + }, + "links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/link" + } + } + } + }, + "exception": { + "type": "object", + "required": [ + "code" + ], + "properties": { + "code": { + "type": "string" + }, + "description": { + "type": "string" + } + } + }, + "confClasses": { + "type": "object", + "required": [ + "conformsTo" + ], + "properties": { + "conformsTo": { + "type": "array", + "items": { + "type": "string", + "example": [ + "http://www.opengis.net/spec/ogcapi-movingfeatures-1/1.0/conf/common", + "http://www.opengis.net/spec/ogcapi-movingfeatures-1/1.0/conf/mf-collection", + "http://www.opengis.net/spec/ogcapi-movingfeatures-1/1.0/conf/movingfeatures" + ] + } + } + } + }, + "extent": { + "description": "The extent of the features in the collection. In the Core only spatial and temporal\nextents are specified. Extensions may add additional members to represent other\nextents, for example, thermal or pressure ranges.\n\nAn array of extents is provided for each extent type (spatial, temporal). The first item\nin the array describes the overall extent of the data. All subsequent items describe more\nprecise extents, e.g., to identify clusters of data. Clients only interested in the\noverall extent will only need to access the first extent in the array.", + "type": "object", + "properties": { + "spatial": { + "description": "The spatial extent of the features in the collection.", + "type": "object", + "properties": { + "bbox": { + "description": "One or more bounding boxes that describe the spatial extent of the dataset.\nIn the Core only a single bounding box is supported.\n\nExtensions may support additional areas.\nThe first bounding box describes the overall spatial\nextent of the data. All subsequent bounding boxes describe\nmore precise bounding boxes, e.g., to identify clusters of data.\nClients only interested in the overall spatial extent will\nonly need to access the first bounding box in the array.", + "type": "array", + "minItems": 1, + "items": { + "description": "Each bounding box is provided as four or six numbers, depending on\nwhether the coordinate reference system includes a vertical axis\n(height or depth):\n\n* Lower left corner, coordinate axis 1\n* Lower left corner, coordinate axis 2\n* Minimum value, coordinate axis 3 (optional)\n* Upper right corner, coordinate axis 1\n* Upper right corner, coordinate axis 2\n* Maximum value, coordinate axis 3 (optional)\n\nIf the value consists of four numbers, the coordinate reference system is\nWGS 84 longitude/latitude (http://www.opengis.net/def/crs/OGC/1.3/CRS84)\nunless a different coordinate reference system is specified in `crs`.\n\nIf the value consists of six numbers, the coordinate reference system is WGS 84\nlongitude/latitude/ellipsoidal height (http://www.opengis.net/def/crs/OGC/0/CRS84h)\nunless a different coordinate reference system is specified in `crs`.\n\nFor WGS 84 longitude/latitude the values are in most cases the sequence of\nminimum longitude, minimum latitude, maximum longitude and maximum latitude.\nHowever, in cases where the box spans the antimeridian the first value\n(west-most box edge) is larger than the third value (east-most box edge).\n\nIf the vertical axis is included, the third and the sixth number are\nthe bottom and the top of the 3-dimensional bounding box.\n\nIf a feature has multiple spatial geometry properties, it is the decision of the\nserver whether only a single spatial geometry property is used to determine\nthe extent or all relevant geometries.", + "type": "array", + "oneOf": [ + { + "minItems": 4, + "maxItems": 4 + }, + { + "minItems": 6, + "maxItems": 6 + } + ], + "items": { + "type": "number" + }, + "example": [ + -180, + -90, + 180, + 90 + ] + } + }, + "crs": { + "description": "Coordinate reference system of the coordinates in the spatial extent\n(property `bbox`). The default reference system is WGS 84 longitude/latitude.\nIn the Core the only other supported coordinate reference system is\nWGS 84 longitude/latitude/ellipsoidal height for coordinates with height.\nExtensions may support additional coordinate reference systems and add\nadditional enum values.", + "type": "string", + "enum": [ + "http://www.opengis.net/def/crs/OGC/1.3/CRS84", + "http://www.opengis.net/def/crs/OGC/0/CRS84h" + ], + "default": "http://www.opengis.net/def/crs/OGC/1.3/CRS84" + } + } + }, + "temporal": { + "description": "The temporal extent of the features in the collection.", + "type": "object", + "properties": { + "interval": { + "description": "One or more time intervals that describe the temporal extent of the dataset.\nIn the Core only a single time interval is supported.\n\nExtensions may support multiple intervals.\nThe first time interval describes the overall\ntemporal extent of the data. All subsequent time intervals describe\nmore precise time intervals, e.g., to identify clusters of data.\nClients only interested in the overall temporal extent will only need\nto access the first time interval in the array (a pair of lower and upper\nbound instants).", + "type": "array", + "minItems": 1, + "items": { + "description": "Begin and end times of the time interval. The timestamps are in the\ntemporal coordinate reference system specified in `trs`. By default\nthis is the Gregorian calendar.\n\nThe value `null` at start or end is supported and indicates a half-bounded interval.", + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "example": [ + "2011-11-11T12:22:11Z", + null + ] + } + }, + "trs": { + "description": "Coordinate reference system of the coordinates in the temporal extent\n(property `interval`). The default reference system is the Gregorian calendar.\nIn the Core this is the only supported temporal coordinate reference system.\nExtensions may support additional temporal coordinate reference systems and add\nadditional enum values.", + "type": "string", + "enum": [ + "http://www.opengis.net/def/uom/ISO-8601/0/Gregorian" + ], + "default": "http://www.opengis.net/def/uom/ISO-8601/0/Gregorian" + } + } + } + } + }, + "collection": { + "type": "object", + "required": [ + "id", + "links", + "itemType" + ], + "properties": { + "id": { + "description": "identifier of the collection used, for example, in URIs", + "type": "string", + "example": "address" + }, + "title": { + "description": "human readable title of the collection", + "type": "string", + "example": "address" + }, + "description": { + "description": "a description of the features in the collection", + "type": "string", + "example": "An address." + }, + "links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/link" + }, + "example": [ + { + "href": "https://data.example.com/buildings", + "rel": "item" + }, + { + "href": "https://example.com/concepts/buildings.html", + "rel": "describedby", + "type": "text/html" + } + ] + }, + "extent": { + "$ref": "#/components/schemas/extent" + }, + "itemType": { + "description": "indicator about the type of the items in the collection", + "type": "string", + "default": "movingfeature" + }, + "crs": { + "description": "the list of coordinate reference systems supported by the service", + "type": "array", + "items": { + "type": "string" + }, + "default": [ + "https://www.opengis.net/def/crs/OGC/1.3/CRS84" + ], + "example": [ + "https://www.opengis.net/def/crs/OGC/1.3/CRS84", + "https://www.opengis.net/def/crs/EPSG/0/4326" + ] + }, + "updateFrequency": { + "description": "a time interval of sampling location. The unit is millisecond.", + "type": "number" + } + } + }, + "collections": { + "type": "object", + "required": [ + "collections", + "links" + ], + "properties": { + "collections": { + "type": "array", + "items": { + "$ref": "#/components/schemas/collection" + } + }, + "links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/link" + } + } + } + }, + "collection-body": { + "type": "object", + "required": [ + "itemType" + ], + "properties": { + "title": { + "description": "human readable title of the collection", + "type": "string" + }, + "updateFrequency": { + "description": "a time interval of sampling location. The unit is millisecond.", + "type": "number" + }, + "description": { + "description": "any description", + "type": "string" + }, + "itemType": { + "description": "indicator about the type of the items in the moving features collection (the default value is 'movingfeature').", + "type": "string", + "default": "movingfeature" + } + } + }, + "motionCurve": { + "description": "MF-JSON Prism encoding MotionCurve Object", + "title": "MF-JSON MotionCurve", + "oneOf": [ + { + "type": "string", + "enum": [ + "Discrete", + "Step", + "Linear", + "Quadratic", + "Cubic" + ], + "default": "Linear" + }, + { + "type": "string", + "format": "uri" + } + ] + }, + "namedCRS": { + "description": "MF-JSON Prism encoding NamedCRS Object", + "title": "MF-JSON NamedCRS", + "type": "object", + "nullable": true, + "required": [ + "type", + "properties" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Name" + ] + }, + "properties": { + "type": "object", + "additionalProperties": false, + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "default": "urn:ogc:def:crs:OGC:1.3:CRS84" + } + } + } + } + }, + "linkedCRS": { + "description": "MF-JSON Prism encoding LinkedCRS Object", + "title": "MF-JSON LinkedCRS", + "type": "object", + "nullable": true, + "required": [ + "type", + "properties" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Link" + ] + }, + "properties": { + "type": "object", + "required": [ + "href", + "type" + ], + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "type": { + "type": "string" + } + } + } + } + }, + "crs": { + "description": "MF-JSON Prism encoding CoordinateReferenceSystem Object", + "title": "MF-JSON CRS", + "oneOf": [ + { + "$ref": "#/components/schemas/namedCRS" + }, + { + "$ref": "#/components/schemas/linkedCRS" + } + ] + }, + "trs": { + "description": "The \"trs\" member in MovingFeature object", + "title": "MF-JSON TRS", + "oneOf": [ + { + "$ref": "#/components/schemas/linkedCRS" + }, + { + "type": "object", + "nullable": true, + "required": [ + "type", + "properties" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Name" + ] + }, + "properties": { + "type": "object", + "additionalProperties": false, + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "default": "urn:ogc:data:time:iso8601" + } + } + } + } + } + ] + }, + "temporalPrimitiveGeometry": { + "description": "MF-JSON Prism encoding TemporalPrimitiveGeometry Object", + "title": "MF-JSON TemporalPrimitiveGeometry", + "type": "object", + "required": [ + "type", + "coordinates", + "datetimes" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MovingPoint", + "MovingLineString", + "MovingPolygon", + "MovingPointCloud" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "oneOf": [ + { + "title": "pointGeoJSON coordinates", + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + }, + { + "title": "linestringGeoJSON coordinates", + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + { + "title": "polygonGeoJSON coordinates", + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + { + "title": "multipointGeoJSON coordinates", + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + ] + } + }, + "datetimes": { + "type": "array", + "uniqueItems": true, + "minItems": 2, + "items": { + "type": "string" + } + }, + "interpolation": { + "$ref": "#/components/schemas/motionCurve" + }, + "base": { + "type": "object", + "nullable": true, + "required": [ + "href", + "type" + ], + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "type": { + "type": "string" + } + } + }, + "orientations": { + "type": "array", + "nullable": true, + "items": { + "type": "object", + "required": [ + "scales", + "angles" + ], + "properties": { + "scales": { + "type": "array", + "oneOf": [ + { + "minItems": 2, + "maxItems": 2 + }, + { + "minItems": 3, + "maxItems": 3 + } + ], + "items": { + "type": "number" + } + }, + "angles": { + "type": "array", + "oneOf": [ + { + "minItems": 2, + "maxItems": 2 + }, + { + "minItems": 3, + "maxItems": 3 + } + ], + "items": { + "type": "number" + } + } + } + } + }, + "crs": { + "$ref": "#/components/schemas/crs" + }, + "trs": { + "$ref": "#/components/schemas/trs" + } + } + }, + "temporalComplexGeometry": { + "description": "MF-JSON Prism encoding TemporalComplexGeometry Object", + "title": "MF-JSON TemporalComplexGeometry", + "type": "object", + "required": [ + "type", + "prisms" + ], + "properties": { + "type": { + "type": "string", + "default": "MovingGeometryCollection" + }, + "prisms": { + "type": "array", + "items": { + "$ref": "#/components/schemas/temporalPrimitiveGeometry" + } + }, + "crs": { + "$ref": "#/components/schemas/crs" + }, + "trs": { + "$ref": "#/components/schemas/trs" + } + } + }, + "temporalGeometry": { + "description": "MF-JSON Prism encoding TemporalGeometry Object", + "title": "MF-JSON TemporalGeometry", + "oneOf": [ + { + "$ref": "#/components/schemas/temporalPrimitiveGeometry" + }, + { + "$ref": "#/components/schemas/temporalComplexGeometry" + } + ] + }, + "parametricValues": { + "description": "MF-JSON Prism encoding ParametricValues Object", + "title": "MF-JSON ParametricValues", + "type": "object", + "required": [ + "datetimes" + ], + "properties": { + "datetimes": { + "type": "array", + "uniqueItems": true, + "minItems": 2, + "items": { + "type": "string", + "format": "date-time" + } + } + }, + "additionalProperties": { + "oneOf": [ + { + "type": "object", + "required": [ + "type", + "values" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Measure" + ] + }, + "values": { + "type": "array", + "nullable": true, + "items": { + "type": "number" + } + }, + "interpolation": { + "oneOf": [ + { + "type": "string", + "enum": [ + "Discrete", + "Step", + "Linear", + "Regression" + ], + "default": "Linear" + }, + { + "type": "string", + "format": "uri" + } + ] + }, + "description": { + "type": "string" + }, + "form": { + "oneOf": [ + { + "type": "string", + "minLength": 3, + "maxLength": 3 + }, + { + "type": "string", + "format": "uri" + } + ] + } + } + }, + { + "type": "object", + "required": [ + "type", + "values" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Text" + ] + }, + "values": { + "type": "array", + "nullable": true, + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "boolean" + } + ] + } + }, + "interpolation": { + "oneOf": [ + { + "type": "string", + "enum": [ + "Discrete", + "Step", + "Linear", + "Regression" + ], + "default": "Linear" + }, + { + "type": "string", + "format": "uri" + } + ] + }, + "description": { + "type": "string" + } + } + }, + { + "type": "object", + "required": [ + "type", + "values" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Image" + ] + }, + "values": { + "type": "array", + "nullable": true, + "items": { + "type": "string" + } + }, + "interpolation": { + "oneOf": [ + { + "type": "string", + "enum": [ + "Discrete", + "Step", + "Linear", + "Regression" + ], + "default": "Linear" + }, + { + "type": "string", + "format": "uri" + } + ] + }, + "description": { + "type": "string" + } + } + } + ] + } + }, + "temporalProperties-mfjson": { + "description": "MF-JSON Prism encoding TemporalProperties Object", + "title": "MF-JSON TemporalProperties", + "type": "array", + "nullable": true, + "items": { + "$ref": "#/components/schemas/parametricValues" + } + }, + "bbox": { + "description": "MF-JSON Prism encoding BoundingBox Object", + "title": "MF-JSON BoundingBox", + "type": "array", + "minItems": 4, + "nullable": true, + "items": { + "type": "number" + } + }, + "lifeSpan": { + "description": "MF-JSON Prism encoding LifeSpan Object", + "title": "MF-JSON LifeSpan", + "type": "array", + "minItems": 2, + "maxItems": 2, + "uniqueItems": true, + "items": { + "type": "string", + "nullable": true + } + }, + "pointGeoJSON": { + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Point" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "multipointGeoJSON": { + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPoint" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + } + }, + "linestringGeoJSON": { + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "LineString" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + } + }, + "multilinestringGeoJSON": { + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiLineString" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + } + } + }, + "polygonGeoJSON": { + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Polygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + } + } + }, + "multipolygonGeoJSON": { + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPolygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + } + } + } + }, + "geometryGeoJSON": { + "oneOf": [ + { + "$ref": "#/components/schemas/pointGeoJSON" + }, + { + "$ref": "#/components/schemas/multipointGeoJSON" + }, + { + "$ref": "#/components/schemas/linestringGeoJSON" + }, + { + "$ref": "#/components/schemas/multilinestringGeoJSON" + }, + { + "$ref": "#/components/schemas/polygonGeoJSON" + }, + { + "$ref": "#/components/schemas/multipolygonGeoJSON" + }, + { + "$ref": "#/components/schemas/geometrycollectionGeoJSON" + } + ] + }, + "geometrycollectionGeoJSON": { + "type": "object", + "required": [ + "type", + "geometries" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "GeometryCollection" + ] + }, + "geometries": { + "type": "array", + "items": { + "$ref": "#/components/schemas/geometryGeoJSON" + } + } + } + }, + "movingFeature": { + "type": "object", + "required": [ + "id", + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Feature" + ] + }, + "temporalGeometry": { + "$ref": "#/components/schemas/temporalGeometry" + }, + "temporalProperties": { + "$ref": "#/components/schemas/temporalProperties-mfjson" + }, + "crs": { + "$ref": "#/components/schemas/crs" + }, + "trs": { + "$ref": "#/components/schemas/trs" + }, + "bbox": { + "$ref": "#/components/schemas/bbox" + }, + "time": { + "$ref": "#/components/schemas/lifeSpan" + }, + "geometry": { + "$ref": "#/components/schemas/geometryGeoJSON" + }, + "properties": { + "type": "object", + "nullable": true + }, + "id": { + "description": "An identifier for the feature", + "oneOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/link" + } + } + } + }, + "movingFeatures": { + "type": "object", + "required": [ + "type", + "features" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "FeatureCollection" + ] + }, + "features": { + "type": "array", + "nullable": true, + "items": { + "$ref": "#/components/schemas/movingFeature" + } + }, + "crs": { + "$ref": "#/components/schemas/crs" + }, + "trs": { + "$ref": "#/components/schemas/trs" + }, + "bbox": { + "$ref": "#/components/schemas/bbox" + }, + "time": { + "$ref": "#/components/schemas/lifeSpan" + }, + "links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/link" + } + }, + "timeStamp": { + "type": "string", + "format": "date-time" + }, + "numberMatched": { + "type": "integer", + "minimum": 0 + }, + "numberReturned": { + "type": "integer", + "minimum": 0 + } + } + }, + "movingFeature-mfjson": { + "description": "MF-JSON Prism encoding MovingFeature Object", + "title": "MF-JSON MovingFeature", + "type": "object", + "required": [ + "type", + "temporalGeometry" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Feature" + ] + }, + "temporalGeometry": { + "$ref": "#/components/schemas/temporalGeometry" + }, + "temporalProperties": { + "$ref": "#/components/schemas/temporalProperties-mfjson" + }, + "crs": { + "$ref": "#/components/schemas/crs" + }, + "trs": { + "$ref": "#/components/schemas/trs" + }, + "bbox": { + "$ref": "#/components/schemas/bbox" + }, + "time": { + "$ref": "#/components/schemas/lifeSpan" + }, + "geometry": { + "$ref": "#/components/schemas/geometryGeoJSON" + }, + "properties": { + "type": "object", + "nullable": true + }, + "id": { + "description": "An identifier for the feature", + "oneOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + } + } + }, + "movingFeatureCollection": { + "description": "MF-JSON Prism encoding MovingFeatureCollection Object", + "title": "MF-JSON MovingFeatureCollection", + "type": "object", + "required": [ + "type", + "features" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "FeatureCollection" + ] + }, + "features": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/components/schemas/movingFeature-mfjson" + } + }, + "crs": { + "$ref": "#/components/schemas/crs" + }, + "trs": { + "$ref": "#/components/schemas/trs" + }, + "bbox": { + "$ref": "#/components/schemas/bbox" + }, + "time": { + "$ref": "#/components/schemas/lifeSpan" + }, + "label": { + "type": "string", + "nullable": true + } + } + }, + "temporalGeometrySequence": { + "type": "object", + "required": [ + "type", + "geometrySequence" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "TemporalGeometrySequence" + ] + }, + "geometrySequence": { + "type": "array", + "items": { + "$ref": "#/components/schemas/temporalPrimitiveGeometry" + } + }, + "links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/link" + } + }, + "timeStamp": { + "type": "string", + "format": "date-time" + }, + "numberMatched": { + "type": "integer", + "minimum": 0 + }, + "numberReturned": { + "type": "integer", + "minimum": 0 + } + } + }, + "temporalPrimitiveValue": { + "type": "object", + "required": [ + "datetimes", + "values", + "interpolation" + ], + "properties": { + "datetimes": { + "type": "array", + "uniqueItems": true, + "minItems": 2, + "items": { + "type": "string", + "format": "date-time" + } + }, + "values": { + "oneOf": [ + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + } + ] + }, + "interpolation": { + "type": "string", + "enum": [ + "Discrete", + "Step", + "Linear", + "Regression" + ] + } + } + }, + "temporalProperty": { + "type": "object", + "required": [ + "name", + "type" + ], + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "TBoolean", + "TText", + "TInteger", + "TReal", + "TImage" + ] + }, + "form": { + "oneOf": [ + { + "type": "string", + "format": "uri" + }, + { + "type": "string", + "minLength": 3, + "maxLength": 3 + } + ] + }, + "valueSequence": { + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/components/schemas/temporalPrimitiveValue" + } + }, + "description": { + "type": "string" + }, + "links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/link" + } + } + } + }, + "temporalProperties": { + "type": "object", + "required": [ + "temporalProperties" + ], + "properties": { + "temporalProperties": { + "oneOf": [ + { + "$ref": "#/components/schemas/temporalProperties-mfjson" + }, + { + "type": "array", + "items": { + "$ref": "#/components/schemas/temporalProperty" + } + } + ] + }, + "links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/link" + } + }, + "timeStamp": { + "type": "string", + "format": "date-time" + }, + "numberMatched": { + "type": "integer", + "minimum": 0 + }, + "numberReturned": { + "type": "integer", + "minimum": 0 + } + } + } + }, + "responses": { + "LandingPage": { + "description": "The links to the API capabilities.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/landingPage" + } + } + } + }, + "ServerError": { + "description": "A server error occurred.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/exception" + }, + "example": { + "code": "500", + "description": "Server Internal Error" + } + } + } + }, + "Conformance": { + "description": "The URIs of all requirements classes supported by the server.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/confClasses" + } + } + } + }, + "Collections": { + "description": "A list of catalogs about collections of moving features.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/collections" + }, + "example": { + "collections": [ + { + "id": "mfc-1", + "title": "MovingFeatureCollection_1", + "description": "a collection of moving features to manage data in a distinct (physical or logical) space", + "itemType": "movingfeature", + "updateFrequency": 1000, + "extent": { + "spatial": { + "bbox": [ + -180, + -90, + 190, + 90 + ], + "crs": "http://www.opengis.net/def/crs/OGC/1.3/CRS84" + }, + "temporal": { + "interval": [ + "2011-11-11T12:22:11Z", + "2012-11-24T12:32:43Z" + ], + "trs": "http://www.opengis.net/def/uom/ISO-8601/0/Gregorian" + } + }, + "links": [ + { + "href": "https://data.example.org/collections/mfc-1", + "rel": "self", + "type": "application/json" + } + ] + } + ], + "links": [ + { + "href": "https://data.example.org/collections", + "rel": "self", + "type": "application/json" + } + ] + } + } + } + }, + "Collection": { + "description": "The metadata being returned.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/collection" + }, + "example": { + "id": "mfc-1", + "title": "moving_feature_collection_sample", + "itemType": "movingfeature", + "updateFrequency": 1000, + "extent": { + "spatial": { + "bbox": [ + -180, + -90, + 190, + 90 + ], + "crs": [ + "http://www.opengis.net/def/crs/OGC/1.3/CRS84" + ] + }, + "temporal": { + "interval": [ + "2011-11-11T12:22:11Z", + "2012-11-24T12:32:43Z" + ], + "trs": [ + "http://www.opengis.net/def/uom/ISO-8601/0/Gregorian" + ] + } + }, + "links": [ + { + "href": "https://data.example.org/collections/mfc-1", + "rel": "self", + "type": "application/json" + } + ] + } + } + } + }, + "MovingFeatures": { + "description": "A list of static data of moving feature.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/movingFeatures" + }, + "example": { + "type": "FeatureCollection", + "features": [ + { + "id": "mf-1", + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [ + [ + 139.757083, + 35.627701, + 0.5 + ], + [ + 139.757399, + 35.627701, + 2 + ], + [ + 139.757555, + 35.627688, + 4 + ], + [ + 139.757651, + 35.627596, + 4 + ], + [ + 139.757716, + 35.627483, + 4 + ] + ] + }, + "properties": { + "label": "car", + "state": "test1", + "video": "http://www.opengis.net/spec/movingfeatures/json/1.0/prism/example/video.mpeg" + }, + "bbox": [ + 139.757083, + 35.627483, + 0, + 139.757716, + 35.627701, + 4.5 + ], + "time": [ + "2011-07-14T22:01:01Z", + "2011-07-15T01:11:22Z" + ], + "crs": { + "type": "Name", + "properties": "urn:ogc:def:crs:OGC:1.3:CRS84" + }, + "trs": { + "type": "Name", + "properties": "urn:ogc:data:time:iso8601" + } + } + ], + "crs": { + "type": "Name", + "properties": "urn:ogc:def:crs:OGC:1.3:CRS84" + }, + "trs": { + "type": "Name", + "properties": "urn:ogc:data:time:iso8601" + }, + "links": [ + { + "href": "https://data.example.org/collections/mfc-1/items", + "rel": "self", + "type": "application/geo+json" + }, + { + "href": "https://data.example.org/collections/mfc-1/items&offset=1&limit=1", + "rel": "next", + "type": "application/geo+json" + } + ], + "timeStamp": "2020-01-01T12:00:00Z", + "numberMatched": 100, + "numberReturned": 1 + } + } + } + }, + "MovingFeature": { + "description": "A moving feature static data.", + "content": { + "application/geo+json": { + "schema": { + "$ref": "#/components/schemas/movingFeature" + }, + "example": { + "id": "mf-1", + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [ + [ + 139.757083, + 35.627701, + 0.5 + ], + [ + 139.757399, + 35.627701, + 2 + ], + [ + 139.757555, + 35.627688, + 4 + ], + [ + 139.757651, + 35.627596, + 4 + ], + [ + 139.757716, + 35.627483, + 4 + ] + ] + }, + "properties": { + "name": "car1", + "state": "test1", + "video": "http://www.opengis.net/spec/movingfeatures/json/1.0/prism/example/video.mpeg" + }, + "bbox": [ + 139.757083, + 35.627483, + 0, + 139.757716, + 35.627701, + 4.5 + ], + "time": [ + "2011-07-14T22:01:01Z", + "2011-07-15T01:11:22Z" + ], + "crs": { + "type": "Name", + "properties": "urn:ogc:def:crs:OGC:1.3:CRS84" + }, + "trs": { + "type": "Name", + "properties": "urn:ogc:data:time:iso8601" + } + } + } + } + }, + "TemporalGeometrySequence": { + "description": "A TemporalGeometrySequence data.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/temporalGeometrySequence" + }, + "example": { + "type": "TemporalGeometrySequence", + "geometrySequence": [ + { + "id": "tg-1", + "type": "MovingPoint", + "datetimes": [ + "2011-07-14T22:01:02Z", + "2011-07-14T22:01:03Z", + "2011-07-14T22:01:04Z" + ], + "coordinates": [ + [ + 139.757399, + 35.627701, + 2 + ], + [ + 139.757555, + 35.627688, + 4 + ], + [ + 139.757651, + 35.627596, + 4 + ] + ], + "interpolation": "Linear", + "base": { + "type": "glTF", + "href": "https://www.opengis.net/spec/movingfeatures/json/1.0/prism/example/car3dmodel.gltf" + }, + "orientations": [ + { + "scales": [ + 1, + 1, + 1 + ], + "angles": [ + 0, + 355, + 0 + ] + }, + { + "scales": [ + 1, + 1, + 1 + ], + "angles": [ + 0, + 0, + 330 + ] + }, + { + "scales": [ + 1, + 1, + 1 + ], + "angles": [ + 0, + 0, + 300 + ] + } + ], + "crs": { + "type": "Name", + "properties": "urn:ogc:def:crs:OGC:1.3:CRS84" + }, + "trs": { + "type": "Name", + "properties": "urn:ogc:data:time:iso8601" + } + } + ], + "links": [ + { + "href": "https://data.example.org/collections/mfc-1/items/mf-1/tgsequence", + "rel": "self", + "type": "application/json" + }, + { + "href": "https://data.example.org/collections/mfc-1/items/mf-1/tgsequence&offset=10&limit=1", + "rel": "next", + "type": "application/json" + } + ], + "timeStamp": "2021-09-01T12:00:00Z", + "numberMatched": 100, + "numberReturned": 1 + } + } + } + }, + "DistanceQuery": { + "description": "A temporal property data that represents a time-to-distance curve of specified temporal primitive geometry.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/temporalProperty" + }, + "example": { + "name": "distance", + "type": "TReal", + "form": "MTR", + "valueSequence": [ + { + "datetimes": [ + "2011-07-15T08:00:00Z", + "2011-07-15T08:00:01Z", + "2011-07-15T08:00:02Z" + ], + "values": [ + 0, + 10, + 20 + ], + "interpolation": "Linear" + } + ] + } + } + } + }, + "VelocityQuery": { + "description": "A temporal property data that represents a time-to-velocity curve of specified temporal primitive geometry.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/temporalProperty" + }, + "example": { + "name": "velocity", + "type": "TReal", + "form": "KMH", + "valueSequence": [ + { + "datetimes": [ + "2011-07-15T08:00:00Z", + "2011-07-15T08:00:01Z", + "2011-07-15T08:00:02Z" + ], + "values": [ + 0, + 10, + 20 + ], + "interpolation": "Linear" + } + ] + } + } + } + }, + "AccelerationQuery": { + "description": "A temporal property data that represents a time-to-acceleration curve of specified temporal primitive geometry.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/temporalProperty" + }, + "example": { + "name": "acceleration", + "type": "TReal", + "form": "KMH", + "valueSequence": [ + { + "datetimes": [ + "2011-07-15T08:00:00Z", + "2011-07-15T08:00:01Z", + "2011-07-15T08:00:02Z" + ], + "values": [ + 0, + 10, + 20 + ], + "interpolation": "Linear" + } + ] + } + } + } + }, + "TemporalProperties": { + "description": "A list of static (or temporal) data of TemporalProperty.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/temporalProperties" + }, + "example": { + "temporalProperties": [ + { + "datetimes": [ + "2011-07-14T22:01:06.000Z", + "2011-07-14T22:01:07.000Z", + "2011-07-14T22:01:08.000Z" + ], + "length": { + "type": "Measure", + "form": "http://qudt.org/vocab/quantitykind/Length", + "values": [ + 1, + 2.4, + 1 + ], + "interpolation": "Linear" + }, + "speed": { + "type": "Measure", + "form": "KMH", + "values": [ + 65, + 70, + 80 + ], + "interpolation": "Linear" + } + } + ], + "links": [ + { + "href": "https://data.example.org/collections/mfc-1/items/mf-1/tproperties", + "rel": "self", + "type": "application/json" + }, + { + "href": "https://data.example.org/collections/mfc-1/items/mf-1/tproperties&offset=2&limit=2", + "rel": "next", + "type": "application/json" + } + ], + "timeStamp": "2021-09-01T12:00:00Z", + "numberMatched": 10, + "numberReturned": 2 + } + } + } + }, + "TemporalProperty": { + "description": "A (subsequence of) the temporal property data.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/temporalProperty" + }, + "example": { + "name": "speed", + "type": "TReal", + "form": "KMH", + "valueSequence": [ + { + "datetimes": [ + "2011-07-15T08:00:00Z", + "2011-07-15T08:00:01Z", + "2011-07-15T08:00:02Z" + ], + "values": [ + 0, + 20, + 50 + ], + "interpolation": "Linear" + } + ] + } + } + } + } + }, + "parameters": { + "collectionId": { + "name": "collectionId", + "in": "path", + "description": "local identifier of a collection", + "required": true, + "schema": { + "type": "string" + } + }, + "bbox": { + "name": "bbox", + "in": "query", + "description": "Only features that have a geometry that intersects the bounding box are selected.\nThe bounding box is provided as four or six numbers, depending on whether the\ncoordinate reference system includes a vertical axis (height or depth):\n\n* Lower left corner, coordinate axis 1\n* Lower left corner, coordinate axis 2\n* Minimum value, coordinate axis 3 (optional)\n* Upper right corner, coordinate axis 1\n* Upper right corner, coordinate axis 2\n* Maximum value, coordinate axis 3 (optional)\n\nIf the value consists of four numbers, the coordinate reference system is\nWGS 84 longitude/latitude (http://www.opengis.net/def/crs/OGC/1.3/CRS84)\nunless a different coordinate reference system is specified in the parameter `bbox-crs`.\n\nIf the value consists of six numbers, the coordinate reference system is WGS 84 \nlongitude/latitude/ellipsoidal height (http://www.opengis.net/def/crs/OGC/0/CRS84h)\nunless a different coordinate reference system is specified in the parameter `bbox-crs`.\n\nThe query parameter `bbox-crs` is specified in OGC API - Features - Part 2: Coordinate \nReference Systems by Reference.\n\nFor WGS 84 longitude/latitude the values are in most cases the sequence of\nminimum longitude, minimum latitude, maximum longitude and maximum latitude.\nHowever, in cases where the box spans the antimeridian the first value\n(west-most box edge) is larger than the third value (east-most box edge).\n\nIf the vertical axis is included, the third and the sixth number are the\nbottom and the top of the 3-dimensional bounding box.\n\nIf a feature has multiple spatial geometry properties, it is the decision of the\nserver whether only a single spatial geometry property is used to determine\nthe extent or all relevant geometries.", + "required": false, + "schema": { + "type": "array", + "oneOf": [ + { + "minItems": 4, + "maxItems": 4 + }, + { + "minItems": 6, + "maxItems": 6 + } + ], + "items": { + "type": "number" + } + }, + "style": "form", + "explode": false + }, + "datetime": { + "name": "datetime", + "in": "query", + "description": "Either a date-time or an interval. Date and time expressions adhere to RFC 3339. \nIntervals may be bounded or half-bounded (double-dots at start or end).\n\nExamples:\n\n* A date-time: \"2018-02-12T23:20:50Z\"\n* A bounded interval: \"2018-02-12T00:00:00Z/2018-03-18T12:31:12Z\"\n* Half-bounded intervals: \"2018-02-12T00:00:00Z/..\" or \"../2018-03-18T12:31:12Z\"\n\nOnly features that have temporal information that intersects the value of `datetime` are selected.\n\nIf a feature has multiple temporal properties, it is the decision of the server whether only a single temporal property is used to determine the extent or all relevant temporal properties.", + "required": true, + "schema": { + "type": "string" + }, + "style": "form", + "explode": false + }, + "limit": { + "name": "limit", + "in": "query", + "description": "The optional limit parameter limits the number of items that are presented in the response document.\n\nOnly items are counted that are on the first level of the collection in the response document.\nNested objects contained within the explicitly requested items shall not be counted.\n\nMinimum = 1. Maximum = 10000. Default = 10.", + "required": false, + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 10000, + "default": 10 + }, + "style": "form", + "explode": false + }, + "subtrajectory": { + "name": "subTrajectory", + "in": "query", + "required": false, + "description": "The `subTrajectory` parameter is a boolean value used with the `datetime` parameter.\nIf the `subTrajectory` is \"true\", \n\n* the `datetime` must be a bounded interval, not half-bounded intervals or a date-time. \n* the `datetime` represents a specified time interval (new start time and new end time)\n* only features with a temporal geometry intersecting the given time interval will return.\n \nThe `subTrajectory` query implements *subTrajectory* operation, which is defined in the [OGC Moving Feature Access](https://docs.ogc.org/is/16-120r3/16-120r3.html).\nThis operation returns only a subsequence of temporal geometry within a time interval contained in the `datetime` parameter, using interpolated trajectory according to the `interpolation` property.\n \nIf the `subTrajectory` parameter is provided with a `bbox` parameter, it will only apply to resources that intersect with a `bbox` parameter. \n\nThe `subTrajectory` parameter must not be used with the `leaf` parameter. \nOnly one of these parameters can be used in the HTTP GET operation.", + "schema": { + "type": "boolean" + }, + "style": "form", + "explode": false + }, + "mFeatureId": { + "name": "mFeatureId", + "in": "path", + "description": "local identifier of a moving feature", + "required": true, + "schema": { + "type": "string" + } + }, + "leaf": { + "name": "leaf", + "in": "query", + "required": false, + "description": "The `leaf` is provided as a sequence of monotonic increasing instants with date-time strings.\nOnly features that have a temporal geometry and property that intersects the given date-time are selected.\n\nThe `leaf` operation implements *_pointAtTime_* operation which defined in the OGC Moving Feature Access.\nThis operation returns only temporal geometry coordinates (or temporal property values) \nat each date-time included in the `leaf` parameter, using interpolated trajectory according to the `interpolation` property.\n\nIf the `leaf` parameter is provided with a `bbox` or (and) a `datetime` parameter, \nit will only apply to resources that intersect with a `bbox` or (and) a `datetime` parameter. \n\nThe `leaf` parameter shall not be used with the `subTrajectory` and `subTemporalValue` parameter. \nOnly one of those parameters can be used in the HTTP GET operation.", + "schema": { + "type": "array", + "uniqueItems": true, + "minItems": 1, + "items": { + "type": "string", + "format": "date-time" + } + }, + "style": "form", + "explode": false + }, + "tGeometryId": { + "name": "tGeometryId", + "in": "path", + "description": "local identifier of a temporal primitive geometry", + "required": true, + "schema": { + "type": "string" + } + }, + "subtemporalvalue": { + "name": "subTemporalValue", + "in": "query", + "required": false, + "description": "The `subTemporalValue` parameter is a boolean value used with the `datetime` parameter.\nIf the `subTemporalValue` is \"true\", \n\n* the `datetime` must be a bounded interval, not half-bounded intervals or a date-time. \n* the `datetime` represents a specified time interval (new start time and new end time)\n* only features with a temporal property intersecting the given time interval will return.\n* it returns only the subsequence of temporal property value within a time interval contained in the `subTemporalValue` parameter, using an interpolated time-to-value curve of temporal property according to the `interpolation` property.\n\nThe `subTemporalValue` parameter must not be used with the `leaf` parameter. \nOnly one of these parameters can be used in the HTTP GET operation.", + "schema": { + "type": "boolean" + }, + "style": "form", + "explode": false + }, + "tPropertyName": { + "name": "tPropertyName", + "in": "path", + "description": "local identifier of a temporal property", + "required": true, + "schema": { + "type": "string" + } + }, + "tValueId": { + "name": "tValueId", + "in": "path", + "description": "local identifier of a temporal primitive value", + "required": true, + "schema": { + "type": "string" + } + } + } + } +} \ No newline at end of file