opengeospatial / ogcapi-processes

https://ogcapi.ogc.org/processes
Other
46 stars 45 forks source link

Generic Inline value object definition #168

Closed gfenoy closed 3 years ago

gfenoy commented 3 years ago

I would like to ask if we can use this issue as a start for discussing the capability to provide and integrate within Processes such a generic inline value object schema to be used to produce a relevant ProcessDescription ?

pvretano commented 3 years ago

@gfenoy at this late date in the process you need to be VERY SPECIFIC about the changes you would like us to consider. As far as I know, we have already made most (if not all) the changes requested by routes to try and harmonise the two standards.

A good starting point would be this ... In the issue you reference from RAPI you present the definition of an input property "A". Explain in words how you want A to behave and I can try to give you the JSON schema for that based on the current definitions in OAPIP.

bpross-52n commented 3 years ago

@gfenoy Lets discuss this during the next SWG telecon on Monday.

jerstlouis commented 3 years ago

@pvretano Since I think this was one of the changes proposed by routes, I think we need to discuss this.

This is about e.g. the possibility for an execute request to provide for the same input either:

There are further concerns with the Workflows extension. Although the Link object could be used with different relation types to identify an OGC API Collection or an OGC API Process rather than a generic process, the recursiveness for the nested Process option was super important -- whereas the top-level object is the same as a nested process, which also has inputs.

gfenoy commented 3 years ago

I try to add some content for the discussion to happen today.

Let suppose we have a service named Buffer that take two inputs:

The service outputs the buffered geometry named Result.

The first version of the service support the inputGeometry to be provided as a featureCollection that can come as a GML (so using the "application/gml+xml" mediaType) or as GeoJSON (so using the "application/geojson"). It returns the Result using the exact same format.

From my understanding, the following ProcessDescription would be acceptable at the current state of the OGC API - Processes:

{
    "id": "Buffer",
    "title": "Create a buffer around a polygon. ",
    "description": "This service shall return a feature collection representing the buffer of geometry InputPolygon at distance BufferDistance. The buffer of a geometry at distance d is the Polygon or MultiPolygon which contains all points within a distance d of the geometry.",
    "version": "2.0.0",
    "jobControlOptions": [
    "sync-execute",
    "async-execute",
    "dismiss"
    ],
    "outputTransmission": [
    "value",
    "reference"
    ],
    "links": [
    {
        "rel": "execute",
        "type": "application/json",
        "title": "Execute End Point",
        "href": "http://tb17.geolabs.fr:8082/ogc-api/processes/Buffer/execution"
    },
    {
        "rel": "alternate",
        "type": "text/html",
        "title": "Execute End Point",
        "href": "http://tb17.geolabs.fr:8082/ogc-api/processes/Buffer/execution.html"
    }
    ],
    "inputs": {
    "InputPolygon": {
        "title": "Polygon to be buffered",
        "description": "URI to a set of GML that describes the polygon.",
        "schema": {
        "oneOf": [
            {
            "allOf": [
                {
                "$ref": "http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/schemas/link.yaml"
                },
                {
                "type": "object",
                "properties": {
                    "type": "string",
                    "enum": [
                    "application/gml+xml",
                    "application/geojson"
                    ]
                }
                }
            ]
            },
            {
            "type": "string",
            "contentEncoding": "UTF-8",
            "contentMediaType": "application/gml+xml",
            },
            {
            "type": "object"
            }
        ]
        },
        "metadata": [
        {
            "title": "Mon test  "
        }
        ]
    },
    "BufferDistance": {
        "title": "Buffer Distance",
        "description": "Distance to be used to calculate buffer.",
        "minOccurs": 0,
        "schema": {
        "type": "number",
        "default": 10
        }
    }
    },
    "outputs": {
    "Result": {
        "title": "Buffered Polygon",
        "description": "GML stream describing the buffered polygon feature.",
        "schema": {
        "oneOf": [
            {
            "allOf": [
                {
                "$ref": "http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/schemas/link.yaml"
                },
                {
                "type": "object",
                "properties": {
                    "type": "string",
                    "enum": [
                    "application/gml+xml",
                    "application/geojson"
                    ]
                }
                }
            ]
            },
            {
            "type": "string",
            "contentEncoding": "UTF-8",
            "contentMediaType": "application/gml+xml"
            },
            {
            "type": "object"
            }
        ]
        },
        "metadata": [
        {
            "title": "Mon test  "
        }
        ]
    }
    }
}

The corresponding execute request may be the following:

{
    "inputs": {
        "InputPolygon": '<ogr:FeatureCollection     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     xsi:schemaLocation="http://ogr.maptools.org/ toto.xsd"     xmlns:ogr="http://ogr.maptools.org/"     xmlns:gml="http://www.opengis.net/gml">  <gml:boundedBy>    <gml:Box>      <gml:coord><gml:X>-1.525232147746957</gml:X><gml:Y>12.33206089978903</gml:Y></gml:coord>      <gml:coord><gml:X>-1.525232147746957</gml:X><gml:Y>12.33206089978903</gml:Y></gml:coord>    </gml:Box>  </gml:boundedBy> <gml:featureMember>    <ogr:sql_statement fid="sql_statement.0">      <ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>-1.52523214774696,12.332060899789</gml:coordinates></gml:Point></ogr:geometryProperty>    </ogr:sql_statement>  </gml:featureMember></ogr:FeatureCollection>',
        "BufferDistance":  10
    },
    "outputs": {
        "Result": {
            "type": "application/json",
            "transmissionMode": "value"
        }
    }
}

For this example it just works great.

Now, let suppose we want to add a new format to both the inputGeometry and Result that will have "application/vnd.google-earth.kml+xml" mediaType. Then I would like the following to be acceptable.

{
    "id": "Buffer",
    "title": "Create a buffer around a polygon. ",
    "description": "This service shall return a feature collection representing the buffer of geometry InputPolygon at distance BufferDistance. The buffer of a geometry at distance d is the Polygon or MultiPolygon which contains all points within a distance d of the geometry.",
    "version": "2.0.0",
    "jobControlOptions": [
    "sync-execute",
    "async-execute",
    "dismiss"
    ],
    "outputTransmission": [
    "value",
    "reference"
    ],
    "links": [
    {
        "rel": "execute",
        "type": "application/json",
        "title": "Execute End Point",
        "href": "http://tb17.geolabs.fr:8082/ogc-api/processes/Buffer/execution"
    },
    {
        "rel": "alternate",
        "type": "text/html",
        "title": "Execute End Point",
        "href": "http://tb17.geolabs.fr:8082/ogc-api/processes/Buffer/execution.html"
    }
    ],
    "inputs": {
    "InputPolygon": {
        "title": "Polygon to be buffered",
        "description": "URI to a set of GML that describes the polygon.",
        "schema": {
        "oneOf": [
            {
            "allOf": [
                {
                "$ref": "http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/schemas/link.yaml"
                },
                {
                "type": "object",
                "properties": {
                    "type": "string",
                    "enum": [
                    "application/gml+xml",
                    "application/vnd.google-earth.kml+xml",
                    "application/geojson"
                    ]
                }
                }
            ]
            },
            {
            "type": "object",
            "properties": {
                "value": {
                "oneOf": [
                    {
                    "type": "string",
                    "contentEncoding": "utf-8",
                    "contentMediaType": "application/gml+xml"
                    },
                    {
                    "type": "string",
                    "contentEncoding": "utf-8",
                    "contentMediaType": "application/vnd.google-earth.kml+xml"
                    }           
                ]
                },
                "type": {
                "type": "string",
                "enum": [
                    "application/gml+xml",
                    "application/vnd.google-earth.kml+xml"
                ]
                }
            }            
            },
            {
            "type": "object"
            }
        ]
        },
        "metadata": [
        {
            "title": "Mon test  "
        }
        ]
    },
    "BufferDistance": {
        "title": "Buffer Distance",
        "description": "Distance to be used to calculate buffer.",
        "minOccurs": 0,
        "schema": {
        "type": "number",
        "default": 10
        }
    }
    },
    "outputs": {
    "Result": {
        "title": "Buffered Polygon",
        "description": "GML stream describing the buffered polygon feature.",
        "schema": {
        "oneOf": [
            {
            "allOf": [
                {
                "$ref": "http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/schemas/link.yaml"
                },
                {
                "type": "object",
                "properties": {
                    "type": "string",
                    "enum": [
                    "application/gml+xml",
                    "application/vnd.google-earth.kml+xml",
                    "application/json"
                    ]
                }
                }
            ]
            },
            {
            "type": "object",
            "properties": {
                "value": {
                "oneOf": [
                    {
                    "type": "string",
                    "contentEncoding": "utf-8",
                    "contentMediaType": "application/gml+xml"
                    },
                    {
                    "type": "string",
                    "contentEncoding": "utf-8",
                    "contentMediaType": "application/vnd.google-earth.kml+xml"
                    }           
                ]
                },
                "type": {
                "type": "string",
                "enum": [
                    "application/gml+xml",
                    "application/vnd.google-earth.kml+xml"
                ]
                }
            }            
            },
            {
            "type": "object"
            }
        ]
        },
        "metadata": [
        {
            "title": "Mon test  "
        }
        ]
    }
    }
}

With the previous definition, the corresponding execute request may be the following:

{
    "inputs": {
        "InputPolygon": {
            "value":  '<ogr:FeatureCollection     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     xsi:schemaLocation="http://ogr.maptools.org/ toto.xsd"     xmlns:ogr="http://ogr.maptools.org/"     xmlns:gml="http://www.opengis.net/gml">  <gml:boundedBy>    <gml:Box>      <gml:coord><gml:X>-1.525232147746957</gml:X><gml:Y>12.33206089978903</gml:Y></gml:coord>      <gml:coord><gml:X>-1.525232147746957</gml:X><gml:Y>12.33206089978903</gml:Y></gml:coord>    </gml:Box>  </gml:boundedBy> <gml:featureMember>    <ogr:sql_statement fid="sql_statement.0">      <ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>-1.52523214774696,12.332060899789</gml:coordinates></gml:Point></ogr:geometryProperty>    </ogr:sql_statement>  </gml:featureMember></ogr:FeatureCollection>',
            "type": "application/gml+xml"
        },
        "BufferDistance":  10
    },
    "outputs": {
        "Result": {
            "type": "application/json",
            "transmissionMode": "value"
        }
    }
}

So, this time, rather than supporting direct inline value, it uses an object containing the value attribute inline.

Another issue I am facing when implementing the ProcessDescription is that it is very verbose. Let suppose we have a service supporting multiple format for imagery that can be provided in different encoding, let take binary and base64 as example. Then, the integration of the contentEncoding and contentMediaType parameter make the ProcessDescription very long.

You can find bellow an example that would be supported.


{
    "id": "SAGA.imagery_vigra.10",
    "title": "Random Forest Presence Prediction (ViGrA)",
    "description": "Random Forest Presence Prediction (ViGrA)",
    "version": "1.0.0",
    "jobControlOptions": [
    "sync-execute",
    "async-execute",
    "dismiss"
    ],
    "outputTransmission": [
    "value",
    "reference"
    ],
    "links": [
    {
        "rel": "execute",
        "type": "application/json",
        "title": "Execute End Point",
        "href": "http://tb17.geolabs.fr:8082/ogc-api/processes/SAGA.imagery_vigra.10/execution"
    },
    {
        "rel": "alternate",
        "type": "text/html",
        "title": "Execute End Point",
        "href": "http://tb17.geolabs.fr:8082/ogc-api/processes/SAGA.imagery_vigra.10/execution.html"
    }
    ],
    "inputs": {
    "FEATURES": {
        "title": "Features",
        "description": "Features",
        "schema": {
        "type": "array",
        "minItems": 1,
        "maxItems": 1024,
        "items": {
            "oneOf": [
            {
                "allOf": [
                {
                    "$ref": "http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/schemas/link.yaml"
                },
                {
                    "type": "object",
                    "properties": {
                    "type": "string",
                    "enum": [
                        "image/tiff",
                        "application/x-ogc-envi",
                        "application/x-imagewebserver-ecw",
                        "image/png"
                    ]
                    }
                }
                ]
            },
            {
                "type": "object",
                "properties": {
                "value": {
                    "oneOf": [
                    {
                        "type": "string",
                        "contentEncoding": "binary",
                        "contentMediaType": "image/tiff"
                    },
                    {
                        "type": "string",
                        "contentEncoding": "base64",
                        "contentMediaType": "image/tiff"
                    },
                    {
                        "type": "string",
                        "contentEncoding": "binary",
                        "contentMediaType": "application/x-ogc-envi"
                    },
                    {
                        "type": "string",
                        "contentEncoding": "base64",
                        "contentMediaType": "application/x-ogc-envi"
                    },
                    {
                        "type": "string",
                        "contentEncoding": "binary",
                        "contentMediaType": "application/x-imagewebserver-ecw"
                    },
                    {
                        "type": "string",
                        "contentEncoding": "base64",
                        "contentMediaType": "application/x-imagewebserver-ecw"
                    },
                    {
                        "type": "string",
                        "contentEncoding": "binary",
                        "contentMediaType": "image/png"
                    },
                    {
                        "type": "string",
                        "contentEncoding": "base64",
                        "contentMediaType": "image/png"
                    }
                    ]
                },
                "type": {
                    "type": "string",
                    "enum": [
                    "image/tiff",
                    "application/x-ogc-envi",
                    "application/x-imagewebserver-ecw",
                    "image/png"
                    ]
                },
                "encoding": {
                    "type": "string",
                    "enum": [
                    "base64",
                    "binary"
                    ]                       
                }
                }
            }
            ]
        }
        }
    },
    "PRESENCE": {
        "title": "Presence Data",
        "description": "Presence Data",
        "schema": {
        "oneOf": [
            {
            "allOf": [
                {
                "$ref": "http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/schemas/link.yaml"
                },
                {
                "type": "object",
                "properties": {
                    "type": "string",
                    "enum": [
                    "text/xml",
                    "application/vnd.google-earth.kml+xml",
                    "application/json"
                    ]
                }
                }
            ]
            },
            {
            "type": "object",
            "properties": {
                "value": {
                "oneOf": [
                    {
                    "type": "string",
                    "contentEncoding": "utf-8",
                    "contentMediaType": "text/xml"
                    },
                    {
                    "type": "string",
                    "contentEncoding": "utf-8",
                    "contentMediaType": "application/vnd.google-earth.kml+xml"
                    }           
                ]
                },
                "type": {
                "type": "string",
                "enum": [
                    "text/xml",
                    "application/vnd.google-earth.kml+xml"
                ]
                }
            }            
            },
            {
            "type": "object"
            }
        ]
        }
    },
    "BACKGROUND": {
        "title": "Background Sample Density [Percent]",
        "description": "Background Sample Density [Percent]",
        "minOccurs": 0,
        "maxOccurs": 1,
        "schema": {
        "type": "number",
        "default": 1
        }
    },
    "DO_MRMR": {
        "title": "Minimum Redundancy Feature Selection",
        "description": "Use only features selected by the minimum Redundancy Maximum Relevance (mRMR) algorithm",
        "minOccurs": 0,
        "maxOccurs": 1,
        "schema": {
        "type": "bool",
        "default": false
        }
    },
    "mRMR_NFEATURES": {
        "title": "Number of Features",
        "description": "Number of Features",
        "minOccurs": 0,
        "maxOccurs": 1,
        "schema": {
        "type": "integer",
        "default": 50
        }
    },
    "mRMR_DISCRETIZE": {
        "title": "Discretization",
        "description": "uncheck this means no discretizaton (i.e. data is already integer)",
        "minOccurs": 0,
        "maxOccurs": 1,
        "schema": {
        "type": "bool",
        "default": true
        }
    },
    "mRMR_THRESHOLD": {
        "title": "Discretization Threshold",
        "description": "a double number of the discretization threshold; set to 0 to make binarization",
        "minOccurs": 0,
        "maxOccurs": 1,
        "schema": {
        "type": "number",
        "default": 1
        }
    },
    "mRMR_METHOD": {
        "title": "Selection Method",
        "description": "Selection Method",
        "minOccurs": 0,
        "maxOccurs": 1,
        "schema": {
        "type": "string",
        "default": "Mutual Information Difference (MID)",
        "enum": [
            "Mutual Information Difference (MID)",
            "Mutual Information Quotient (MIQ)"
        ]
        }
    },
    "RF_IMPORT": {
        "title": "Import from File",
        "description": "Import from File",
        "minOccurs": 0,
        "schema": {
        "oneOf": [
            {
            "allOf": [
                {
                "$ref": "http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/schemas/link.yaml"
                },
                {
                "type": "object",
                "properties": {
                    "type": "string",
                    "enum": [
                    "text/plain"
                    ]
                }
                }
            ]
            },
            {
            "type": "string",
            "contentEncoding": "utf-8",
            "contentMediaType": "text/plain"
            }
        ]
        }
    },
    "RF_EXPORT": {
        "title": "Export to File",
        "description": "Export to File",
        "minOccurs": 0,
        "schema": {
        "oneOf": [
            {
            "allOf": [
                {
                "$ref": "http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/schemas/link.yaml"
                },
                {
                "type": "object",
                "properties": {
                    "type": "string",
                    "enum": [
                    "text/plain"
                    ]
                }
                }
            ]
            },
            {
            "type": "string",
            "contentEncoding": "utf-8",
            "contentMediaType": "text/plain"
            }
        ]
        }
    },
    "RF_TREE_COUNT": {
        "title": "Tree Count",
        "description": "How many trees to create?",
        "minOccurs": 0,
        "schema": {
        "type": "integer",
        "default": 32
        }
    },
    "RF_TREE_SAMPLES": {
        "title": "Samples per Tree",
        "description": "Specifies the fraction of the total number of samples used per tree for learning.",
        "minOccurs": 0,
        "schema": {
        "type": "number",
        "default": 1
        }
    },
    "RF_REPLACE": {
        "title": "Sample with Replacement",
        "description": "Sample from training population with or without replacement?",
        "minOccurs": 0,
        "schema": {
        "type": "bool",
        "default": true
        }
    },
    "RF_SPLIT_MIN_SIZE": {
        "title": "Minimum Node Split Size",
        "description": "Number of examples required for a node to be split. Choose 1 for complete growing.",
        "minOccurs": 0,
        "schema": {
        "type": "integer",
        "default": 1
        }
    },
    "RF_NODE_FEATURES": {
        "title": "Features per Node",
        "description": "Features per Node",
        "minOccurs": 0,
        "schema": {
        "type": "string",
        "default": "square root",
        "enum": [
            "logarithmic",
            "square root",
            "all"
        ]
        }
    },
    "RF_STRATIFICATION": {
        "title": "Stratification",
        "description": "Specifies stratification strategy. Either none, equal amount of class samples, or proportional to fraction of class samples.",
        "minOccurs": 0,
        "schema": {
        "type": "string",
        "default": "none",
        "enum": [
            "none",
            "equal",
            "proportional"
        ]
        }
    }
    },
    "outputs": {
    "PREDICTION": {
        "title": "Presence Prediction",
        "description": "Presence Prediction",
        "schema": {
        "oneOf": [
            {
            "allOf": [
                {
                "$ref": "http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/schemas/link.yaml"
                },
                {
                "type": "object",
                "properties": {
                    "type": "string",
                    "enum": [
                    "image/tiff",
                    "application/x-ogc-envi",
                    "application/x-imagewebserver-ecw",
                    "image/png"
                    ]
                }
                }
            ]
            },
            {
            "type": "object",
            "properties": {
                "value": {
                "oneOf": [
                    {
                        "type": "string",
                        "contentEncoding": "binary",
                        "contentMediaType": "image/tiff"
                    },
                    {
                        "type": "string",
                        "contentEncoding": "base64",
                        "contentMediaType": "image/tiff"
                    },
                    {
                        "type": "string",
                        "contentEncoding": "binary",
                        "contentMediaType": "application/x-ogc-envi"
                    },
                    {
                        "type": "string",
                        "contentEncoding": "base64",
                        "contentMediaType": "application/x-ogc-envi"
                    },
                    {
                        "type": "string",
                        "contentEncoding": "binary",
                        "contentMediaType": "application/x-imagewebserver-ecw"
                    },
                    {
                        "type": "string",
                        "contentEncoding": "base64",
                        "contentMediaType": "application/x-imagewebserver-ecw"
                    },
                    {
                        "type": "string",
                        "contentEncoding": "binary",
                        "contentMediaType": "image/png"
                    },
                    {
                        "type": "string",
                        "contentEncoding": "base64",
                        "contentMediaType": "image/png"
                    }
                ]
                },
                "type": {
                "type": "string",
                "enum": [
                    "image/tiff",
                    "application/x-ogc-envi",
                    "application/x-imagewebserver-ecw",
                    "image/png"
                ]
                },
                "encoding": {
                "type": "string",
                "enum": [
                    "base64",
                    "binary"
                ]                       
                }
            }           
            }
        ]
        }
    },
    "PROBABILITY": {
        "title": "Presence Probability",
        "description": "Presence Probability",
        "schema": {
        "oneOf": [
            {
            "allOf": [
                {
                "$ref": "http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/schemas/link.yaml"
                },
                {
                "type": "object",
                "properties": {
                    "type": "string",
                    "enum": [
                    "image/tiff",
                    "application/x-ogc-envi",
                    "application/x-imagewebserver-ecw",
                    "image/png"
                    ]
                }
                }
            ]
            },
            {
            "type": "object",
            "properties": {
                "value": {
                "oneOf": [
                    {
                        "type": "string",
                        "contentEncoding": "binary",
                        "contentMediaType": "image/tiff"
                    },
                    {
                        "type": "string",
                        "contentEncoding": "base64",
                        "contentMediaType": "image/tiff"
                    },
                    {
                        "type": "string",
                        "contentEncoding": "binary",
                        "contentMediaType": "application/x-ogc-envi"
                    },
                    {
                        "type": "string",
                        "contentEncoding": "base64",
                        "contentMediaType": "application/x-ogc-envi"
                    },
                    {
                        "type": "string",
                        "contentEncoding": "binary",
                        "contentMediaType": "application/x-imagewebserver-ecw"
                    },
                    {
                        "type": "string",
                        "contentEncoding": "base64",
                        "contentMediaType": "application/x-imagewebserver-ecw"
                    },
                    {
                        "type": "string",
                        "contentEncoding": "binary",
                        "contentMediaType": "image/png"
                    },
                    {
                        "type": "string",
                        "contentEncoding": "base64",
                        "contentMediaType": "image/png"
                    }
                ]
                },
                "type": {
                "type": "string",
                "enum": [
                    "image/tiff",
                    "application/x-ogc-envi",
                    "application/x-imagewebserver-ecw",
                    "image/png"
                ]
                },
                "encoding": {
                "type": "string",
                "enum": [
                    "base64",
                    "binary"
                ]                       
                }
            }
            }
        ]
        }
    }
    }
}

It would be a bit too much to write the execute request manually here. Nevertheless, for the input, we would need to use both type and encoding when providing the input for being able to make the distinction in between supported types and encodings. So, basically, as with the second version of the Buffer service, we an pass an input as an object with value, type and encoding attributes.

So, the proposal is here to have a kind of mixed type for inputs, it can be a string if there is no option available for input mediaType or encoding, an object (or an array with items of the same kind of object) in case options are available for an input. This way if one want to restrict the supported encoding for any reason, then, one can simply set in the metadata that there is only one support encoding.

gfenoy commented 3 years ago

A simplified version of the following:

{
    "id": "Buffer",
    "title": "Create a buffer around a polygon. ",
    "description": "This service shall return a feature collection representing the buffer of geometry InputPolygon at distance BufferDistance. The buffer of a geometry at distance d is the Polygon or MultiPolygon which contains all points within a distance d of the geometry.",
    "version": "2.0.0",
    "jobControlOptions": [
  "sync-execute",
  "async-execute",
  "dismiss"
    ],
    "outputTransmission": [
  "value",
  "reference"
    ],
    "links": [
  {
      "rel": "execute",
      "type": "application/json",
      "title": "Execute End Point",
      "href": "http://tb17.geolabs.fr:8082/ogc-api/processes/Buffer/execution"
  },
  {
      "rel": "alternate",
      "type": "text/html",
      "title": "Execute End Point",
      "href": "http://tb17.geolabs.fr:8082/ogc-api/processes/Buffer/execution.html"
  }
    ],
    "inputs": {
  "InputPolygon": {
      "title": "Polygon to be buffered",
      "description": "URI to a set of GML that describes the polygon.",
      "schema": {
      "oneOf": [
          {
          "allOf": [
              {
              "$ref": "http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/schemas/link.yaml"
              },
              {
              "type": "object",
              "properties": {
                  "type": "string",
                  "enum": [
                  "application/gml+xml",
                  "application/vnd.google-earth.kml+xml",
                  "application/geojson"
                  ]
              }
              }
          ]
          },
          {
          "type": "object",
          "properties": {
              "value": {
              "oneOf": [
                  {
                  "type": "string",
                  "contentEncoding": "utf-8",
                  "contentMediaType": "application/gml+xml"
                  },
                  {
                  "type": "string",
                  "contentEncoding": "utf-8",
                  "contentMediaType": "application/vnd.google-earth.kml+xml"
                  }           
              ]
              },
              "type": {
              "type": "string",
              "enum": [
                  "application/gml+xml",
                  "application/vnd.google-earth.kml+xml"
              ]
              }
          }            
          },
          {
          "type": "object"
          }
      ]
      },
      "metadata": [
      {
          "title": "Mon test  "
      }
      ]
  },
  "BufferDistance": {
      "title": "Buffer Distance",
      "description": "Distance to be used to calculate buffer.",
      "minOccurs": 0,
      "schema": {
      "type": "number",
      "default": 10
      }
  }
    },
    "outputs": {
  "Result": {
      "title": "Buffered Polygon",
      "description": "GML stream describing the buffered polygon feature.",
      "schema": {
      "oneOf": [
          {
          "allOf": [
              {
              "$ref": "http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/schemas/link.yaml"
              },
              {
              "type": "object",
              "properties": {
                  "type": "string",
                  "enum": [
                  "application/gml+xml",
                  "application/vnd.google-earth.kml+xml",
                  "application/json"
                  ]
              }
              }
          ]
          },
          {
          "type": "object",
          "properties": {
              "value": {
              "oneOf": [
                  {
                  "type": "string",
                  "contentEncoding": "utf-8",
                  "contentMediaType": "application/gml+xml"
                  },
                  {
                  "type": "string",
                  "contentEncoding": "utf-8",
                  "contentMediaType": "application/vnd.google-earth.kml+xml"
                  }           
              ]
              },
              "type": {
              "type": "string",
              "enum": [
                  "application/gml+xml",
                  "application/vnd.google-earth.kml+xml"
              ]
              }
          }            
          },
          {
          "type": "object"
          }
      ]
      },
      "metadata": [
      {
          "title": "Mon test  "
      }
      ]
  }
    }
}

With the previous definition, the corresponding execute request may be the following:

{
    "inputs": {
        "InputPolygon": {
            "value":  '<ogr:FeatureCollection     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     xsi:schemaLocation="http://ogr.maptools.org/ toto.xsd"     xmlns:ogr="http://ogr.maptools.org/"     xmlns:gml="http://www.opengis.net/gml">  <gml:boundedBy>    <gml:Box>      <gml:coord><gml:X>-1.525232147746957</gml:X><gml:Y>12.33206089978903</gml:Y></gml:coord>      <gml:coord><gml:X>-1.525232147746957</gml:X><gml:Y>12.33206089978903</gml:Y></gml:coord>    </gml:Box>  </gml:boundedBy> <gml:featureMember>    <ogr:sql_statement fid="sql_statement.0">      <ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>-1.52523214774696,12.332060899789</gml:coordinates></gml:Point></ogr:geometryProperty>    </ogr:sql_statement>  </gml:featureMember></ogr:FeatureCollection>',
            "type": "application/gml+xml"
        },
        "BufferDistance":  10
    },
    "outputs": {
        "Result": {
            "type": "application/json",
            "transmissionMode": "value"
        }
    }
}

may be rewritten as in the following:

{
    "id": "Buffer",
    "title": "Create a buffer around a polygon. ",
    "description": "This service shall return a feature collection representing the buffer of geometry InputPolygon at distance BufferDistance. The buffer of a geometry at distance d is the Polygon or MultiPolygon which contains all points within a distance d of the geometry.",
    "version": "2.0.0",
    "jobControlOptions": [
    "sync-execute",
    "async-execute",
    "dismiss"
    ],
    "outputTransmission": [
    "value",
    "reference"
    ],
    "links": [
    {
        "rel": "execute",
        "type": "application/json",
        "title": "Execute End Point",
        "href": "http://tb17.geolabs.fr:8082/ogc-api/processes/Buffer/execution"
    },
    {
        "rel": "alternate",
        "type": "text/html",
        "title": "Execute End Point",
        "href": "http://tb17.geolabs.fr:8082/ogc-api/processes/Buffer/execution.html"
    }
    ],
    "inputs": {
    "InputPolygon": {
        "title": "Polygon to be buffered",
        "description": "URI to a set of GML that describes the polygon.",
        "schema": {
        "oneOf": [
            {
            "allOf": [
                {
                "$ref": "http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/schemas/link.yaml"
                },
                {
                "type": "object",
                "properties": {
                    "type": "string",
                    "enum": [
                    "application/gml+xml",
                    "application/vnd.google-earth.kml+xml",
                    "application/geojson"
                    ]
                }
                }
            ]
            },
            {
            "type": "object",
            "properties": {
                "value": {
                    "type": "string",
                "contentEncoding": "utf-8",
                "contentMediaType": {
                                   "type": "string",
                   "enum": [
                                      "application/gml+xml",
                                      "application/vnd.google-earth.kml+xml"
                                    }
                                }
                },
                "type": {
                "type": "string",
                "enum": [
                    "application/gml+xml",
                    "application/vnd.google-earth.kml+xml"
                ]
                }
            }            
            },
            {
            "type": "object"
            }
        ]
        },
        "metadata": [
        {
            "title": "Mon test  "
        }
        ]
    },
    "BufferDistance": {
        "title": "Buffer Distance",
        "description": "Distance to be used to calculate buffer.",
        "minOccurs": 0,
        "schema": {
        "type": "number",
        "default": 10
        }
    }
    },
    "outputs": {
    "Result": {
        "title": "Buffered Polygon",
        "description": "GML stream describing the buffered polygon feature.",
        "schema": {
        "oneOf": [
            {
            "allOf": [
                {
                "$ref": "http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/schemas/link.yaml"
                },
                {
                "type": "object",
                "properties": {
                    "type": "string",
                    "enum": [
                    "application/gml+xml",
                    "application/vnd.google-earth.kml+xml",
                    "application/json"
                    ]
                }
                }
            ]
            },
            {
            "type": "object",
            "properties": {
                "value": {
                    "type": "string",
                "contentEncoding": "utf-8",
                "contentMediaType": {
                                   "type": "string",
                   "enum": [
                                      "application/gml+xml",
                                      "application/vnd.google-earth.kml+xml"
                                    }
                                }
                },
                "type": {
                "type": "string",
                "enum": [
                    "application/gml+xml",
                    "application/vnd.google-earth.kml+xml"
                ]
                }
            }            
            },
            {
            "type": "object"
            }
        ]
        },
        "metadata": [
        {
            "title": "Mon test  "
        }
        ]
    }
    }
}

Note that here, I don't take into account the modification about not supporting a JSON object as an input value as it was based on the previous example, I simply did not change that part. Please don't consider it.

Anyway, on the last example we can probably agree that it is finally not that verbose even if there is still some redundancy especially at the value > cotentMediaType and type > enum.

What do other think?

pvretano commented 3 years ago

@gfenoy I'm still not sure I understand what problem you are seeing so let me try this ...

First, lets focus on the the InputGeometry input. This is what you propose (from above without the titles, descriptions, etc.):

"InputPolygon": {
  "schema": {
    "oneOf": [
      {   
        "allOf": [
          {   
            "$ref": "http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/schemas/link.yaml"
          },  
          {   
            "type": "object",
            "properties": {
              "type": "string",
              "enum": [ "application/gml+xml", "application/vnd.google-earth.kml+xml", "application/geojson" ]
            }   
          }   
        ]   
      },  
      {   
        "type": "object",
        "properties": {
          "value": {
            "type": "string",
            "contentEncoding": "utf-8",
            "contentMediaType": {
              "type": "string",
              "enum": [ "application/gml+xml", "application/vnd.google-earth.kml+xml" ]
            }   
          },  
          "type": {
            "type": "string",
            "enum": [ "application/gml+xml", "application/vnd.google-earth.kml+xml" ]
          }   
        }   
      },  
      {   
        "type": "object"
      }   
    ]   
  }
},

This schema basically say that the InputGeometry can be:

NOTE: I am confused by the inclusion of the contentMediaType key in the definition above. I assume that you are trying to constrain the lexical space of contentMediatType to GML or KML but why do you even need this since you already defined the "type" key that does the same thing. Also, I am not sure if this particular bit is valid JSON Schema or not...

I believe that your intent is that we should now, in an execute request, be able to specify InputPolygon like this (using GML as an example):

"InputPolygon": {
   "href": "... some url ...",
   "type": "application/gml+xml"
}

for passing the value by reference, or

"InputPolygon": {
   "value": "... some GML geometry ...",
   "type": "application/gml+xml"
}

for encoding the value inline in the execute request.

I deliberately used the phrase "I believe that your intent is ..." above because if these inputs were encoded using your description above and the execute schema as it is currently defined in the specification they would look like this ...

"InputPolygon": {
   "href": "... some url ...",
   "type": "application/gml+xml"
}
"InputPolygon": {
   "value" {
     "value": "... some GML geometry ...",
     "type": "application/gml+xml"
  }
}

The first one stays the same since inlineOrRefData.yaml say that the value of an input can be a link. However, this in itself is a problem because we are defining the schema for referencing values in two places; in the definition of the InputPolygon and in the inlineOrRefData.yaml schema. The second input would need to be specified as a qualified value. In order to encode the inputs the way I believe that you intended them to be specified, the schema inlineOrRefData.yaml would need to change from this:

oneOf:
  - type: string
  - type: number
  - type: boolean
  - type: array
  - $ref: "bbox.yaml"
  - $ref: "link.yaml"
  - $ref: "qualifiedValue.yaml"

to this

oneOf:
  - type: string
  - type: number
  - type: boolean
  - type: array
  - type: object

which basically implies that if you want to be able to reference a value or use a qualified value you need to include those schema fragments in the definition of the input in the process description as you have show above.

Now, getting back to your example. If I was to define the InputPolygon input according to the latest draft of the specification, it would look like this:

"InputPolygon": {
  "schema": {
    "oneOf": [
      { 
        "type": "string",
        "contentEncoding": "utf-8",
        "contentMediaType": "application/gml+xml"
      },
      { 
        "type": "string",
        "contentEncoding": "utf-8",
        "contentMediaType": "pplication/vnd.google-earth.kml+xml"
      }, 
      {  
         "type": "object"
      }
    ]
  }
},

In the execute request these input values could be encode (by reference and inline) as ...

"InputPolygon": {
   "href": "... some url ...",
   "type": "application/gml+xml"
}

or

"InputPolygon": {
   "value": "... some GML geometry ...",
   "mediaType": "application/gml+xml"
}

The concise definition for InputPolygon is possible because the capability to reference values or include qualified values is built into the execute schema (see inlineOrRefData.yaml) AND the specification includes requirements with rules about using these structures.

So my question is this ... Why are you saying we should encode the schema in the process description the way you are proposing versus what the specification currently says? What issue are you seeing that I am not seeing? On the face of it, it seems what is in the specification right now is more compact and concise.

jerstlouis commented 3 years ago

@pvretano I discussed this in detail with @gfenoy this morning and I think there are two aspects to the problem:

  1. the need for a clarification by slightly restructuring the inlineOrRefData.yaml and execute.yaml schemas so that the actual input value that is matched with the process description input value schema is not mixed up with what is implicitly allowed in lieu of an input value (i.e. the link.yaml and the qualifiedValue.yaml). This would alleviate the confusion about which constructs of the execute request inputs should actually be validated against the process description input.
  2. then to fully support web developers as @gfenoy is hoping to, we would need the additional full-blown OpenAPI process description that we previously talked about. This would actually be easy to describe in a separate conformance class if we wanted to do this either now or later, and I agree would be highly useful to web developers. This would be one OpenAPI document per process, that could be available either at .../processes/{processId}?f=application/vnd.oai.openapi+json or .../processes/{processId}?f=swaggerui or .../processes/{processId}/api. What this API would contain is the .../processes/{processId}/execution resource path where this time the execute request body is not described generically, but fully adapted to the process, by replacing the overly permissive anything goes input value in order to generically support any process (which we will have previously untangled as described in 1 above) with the schema from the process description input value schema.

I will prepare a proposal on how we can address 1 with this slight re-organization of the schema, which won't technically change anything but will make it more obvious in the API documentation which execute request construct the process description input value schema is intended to validate.

I think what needs to be done for 2 is also quite straightforward and could be described as an extra conformance class with a couple requirements and for which we could provide some example, but we may decide to add this later.

Personally, I would not want the existence of this OpenAPI process description conformance class to result in services deciding not to provide the OGC process description, because the latter is much easier for a generic client to parse and understand (unless we're talking about a purely OpenAPI-based client). But offering both would make a lot of sense and allow web developers clueless about OGC API - Processes to use the API.

Note that even if the specification says nothing about the OpenAPI process description, implementers could still provide it and it would be self-explanatory.

fmigneault commented 3 years ago

If such a specification per process becomes a requirement, OGC-API-Processes will need to make sure to do a lot of work to be very specific about what needs to be provided to ensure valid schemas (maybe as much work as the current OGC-API-Process spec itself?).

I'm mostly concerned about Deploy operation possible with the transaction extension and App Packages. This could make the schema be virtually anything, and the quick and dirty workaround would be to return this per-process schema as "any object". That would obviously not be much more useful for web developers than not having the schema at all. This is why, if this initiative is undertaken, it must be very well defined.

jerstlouis commented 3 years ago

@fmigneault First, the core requirement is that some process description must be provided. The OGC Process Description is the only one currently defined, but is its own conformance class. So implementations could always decide in which format they provide the process description.

Secondly, what I have in mind is indeed very well defined, even considering deployed processes. That is, this OpenAPI process description would:

So if we were to define a conformance class for OpenAPI process description, it would specify this in the associated requirements.Therefore, "any object" would not be valid for this per-proces chema (unless the process input description actually says any object is valid). So, it would be useful for web developers :) I don't think it's that much work, those two bullet points basically summarize what the requirements would need to say.

fmigneault commented 3 years ago

@jerstlouis I'm not too worried about ProcessDescription from core which is relatively "simple". It is not the only process description though. It is the versatile executionUnit from transaction extension that will make it hard to define good recommendations about how the schema should be generated since it can be anything (on purpose): https://github.com/opengeospatial/ogcapi-processes/blob/master/extensions/transactions/openapi/schemas/ogcapppkg.yaml

I wouldn't mind to provide a more specific schema for a given process execution body. Generating the JSON sub-schemas of expected value formats for each corresponding id of the inputs/outputs to request an execution would actually be helpful documentation for the user. We can even add execution examples this way, which is nice.

I find having a full OpenAPI with resource paths per process is a bit overkill though. I think only the schema body of the execute request is sufficient. The rest is already centralized in OGC-API-Processes. The process description also already includes a list of links which usually (or at least should) provide the execute endpoint amongst other useful resources related to each process. The execution-schema could be added to the same list of links for convenience.

jerstlouis commented 3 years ago

@fmigneault I'm not sure I understand your concerns with Transactions. Of course a client wishing to deploy a new process needs to be able to deploy all kinds of processes, so the ogcapppkg.yaml references the generic process.yaml / execute.yaml that accepts any type of input. But once the server deploys this as an individual process, this is where an OpenAPI definition tailored to that process could be generated automatically, based on the process description that was provided as part of the application package.

Generating the JSON sub-schemas of expected value formats for each corresponding id of the inputs/outputs to request an execution would actually be helpful documentation for the user. We can even add execution examples this way, which is nice.

Yes, that is exactly the idea and what @gfenoy was hoping for.

I find having a full OpenAPI with resource paths per process is a bit overkill though. I think only the schema body of the execute request is sufficient.

Having a full OpenAPI for each process provides a fully self-documented API that web developers can use to interact with the process without any knowledge of OGC API - Processes. Just having a schema won't cut it. The service could e.g. provide a SwaggerUI page for each individual process, complete with documentation and examples.

fmigneault commented 3 years ago

The part that I'm concerned about is the self-documentation of the ogcapppkg.yaml part which is specific to each server, process, and implementation. I don't think there is any way I could reliably generate an embedded CWL spec within each per-process OpenAPI. That spec is complicated enough by itself, let alone dynamically per-process, and that is only one of the variants that could be under executionUnit. Since the easy workaround would be to just allow any object, I think this adds no benefit for the web developer.

This is why, if this feature becomes something that must be provided, I would rather limit the requirement to only the schema of the execution request, which is the main interface the developer would actually need to interact with. That one is easy to generate regardless of what the App Package contains, because the inputs/outputs of ProcessDescription part is already available.