Model JSON for visual representation
See original GitHub issueThis captures and expands our discussion on Model JSON spec for visual representation and evaluation. The basic idea is that the current spec does not make it easy enough to generate the UI components and the front-end would have to do some tree evaluation and manipulation work. This is an effort to capture and codify the things that will need to be done and how.
This issue is meant to capture the discussion, there is at least one design alternative that needs input.
The main objective is to provide a credible example of NDVI-reclassify-difference calculation that is consistent likely future features.
Model JSON Spec
Model endpoint is going to accept JSON that represent a complete expression tree. The client is responsible for the expression tree and bind its parameters. The client needs to have access to full expression tree in order to show the visual represetnation and to manipulate in response to user actions.
The client is free to add additional tags that make the job of representing the expression tree easier. This tags would be ignored during the expression evaluation.
Endpoints
The request pattern will be something like this:
-
Build expression tree but sourcing existing models
GET: .../library/?q=NDVI
GET: .../library/{model_id}
-
Register expression with the app
POST: .../model_run
->{ <run_id>, <validation_message> }
-
View the model on a map
GET: .../model_run/{run_id}/{z}/{x}/{y}.png
-
View intermidate stages
GET: .../model_run/{run_id}/tag/{tag_id}/{z}/{x}/{y}.png
-
Update the model or parameters
PATCH: .../model_run
->{ <run_id>, <validation_message> }
Parameters
Parameters need to be fully specified at each use-site. Information for display of the expression can be gathered by aggregating over the tree. Parameters may be changed by tree substitution.
Variable tag has to hold information for several contexts:
type
: What kind of thing does this veriable specifymosaic
,scene
,band
: Identifying tags for the variable typelabel
: Name of the original parameter in the modelid
: Identity/Name of the parameter in this model
id
and label
are different because a parameter in the model being specified maybe be reused in several model templates.
We need id
so we can replace all references to a single variable during tree traversal.
We need to keep label
because as we drill down the model we need that name to help make sense of the paramter usage in the subtree (ex: When looking at NDVI
subtree knowing that given mosaic UUID corresponds to red
parameter in NDVI
)
{
"id": "LC8_0_NIR",
"name": "nir",
"type": "raster",
"mosaic": "123e4567-e89b-12d3-a456-426655440001",
"band": 4
}
Parameter types
This list will be expanded as we add more operations but intially parameters types me be:
raster
class-breaks
polygon
double
int
Parameter identies
There may be multiple ways to satisfy a parameter of a given type, like raster
.
For instance a raster tile may secured from:
- project mosaic
- A single scene
- A saved model run
When this is possible the additional tags will be used to source the raster tile.
Source a raster tile from project mosaic:
{
"id": "LC8_0_NIR",
"name": "nir",
"type": "raster",
"mosaic": "123e4567-e89b-12d3-a456-426655440000",
"band": 4
}
// source a raster tile from a scene
{
"id": "LC8_0_NIR",
"name": "nir",
"type": "raster",
"scene": "123e4567-e89b-12d3-a456-426655440000",
"band": 4
}
// source a raster tile from a scene
{
"id": "LC8_0_NIR",
"name": "nir",
"type": "raster",
"modelRun": "123e4567-e89b-12d3-a456-426655440000",
}
Expression Structure
The expression structure will be composed of nested apply
blocks that represent map algebra operation.
Basic map-algebra operations will always be pre-defined (ex: “+”, “-”, “/”)
{
"apply": "-",
"args": [
{
"type": "raster",
"mosaic": "123e4567-e89b-12d3-a456-426655440000"
},
{
"type": "raster",
"mosaic": "123e4567-e89b-12d3-a456-426655440000"
}
]
}
Model Library
Model library will have a list of model templates that can be combined together. The models in the model library will have the parameters with types defined but they may not have identifying information.
When a model template is inserted into the expression tree it should retain a reference to template record so it can be referenced and shown in the visual representation.
Opaque models
A model may be opaque either because it is private or because it does not have a JSON expression representation.
When this happens the model will be expressed as a model
operation with an id
tag that references the model.
Secret version of reclassify:
{
"apply": "model",
"id": "123e4567-e89b-12d3-a456-426655441111",
"args": {
"breaks": {
"type": "class-breaks"
},
"raster": {
"name": "nir",
"type": "raster"
}
}
}
Named parameters
Some operations require non-raster parameter.
For instance the reclassify
operation requires breaks
parameter.
breaks
corresponds to BreakMap
type in GeoTrellis and specifies where the class boundaries exist and how they are to be handled. (ex: less than, exact match, greater than).
This illustrates:
- We need named parameters to differentiate different param types
- We need to represent and parse corresponding complex object represetnations
{
"apply": "reclassify",
"args": {
"breaks": {
"type": "class-breaks",
"boundaryType": "lessThan",
"classMap": [[3.3, 0], [5.0, 1], [6.1], 2]
},
"raster": {
"name": "nir",
"type": "raster",
"mosaic": "123e4567-e89b-12d3-a456-426655440001",
"band": 4
}
}
}
args
may either be a dictionary or an array, depending on the operation.
As a convention we can say when an operation requires one more parameter of a single type where only the order matters, like raster addition, the args
list will be an array.
Alternativly if the operations arguments are heterogenious or have specific semantic meanings the args
will be a dictionary.
Expression Inspection
A critical feature of the model endpoints is to evaluate and display a subtree of the expression.
In order for this to be possible the client may insert tag
element in any of apply
nodes.
It is up to the client to insure that these tags are unique and possibly meaningful.
The endpoint will provide a way to inspect the expression at the level of a given tag by providing an endpoint like: .../model_run/<model_id>/tag/<tag_id>/{z}/{x}/{y}
Color Maps
Each apply
element may contain colormap
attribute the defines how to render that operation when requested.
NDVI Reclassify Difference
{
"apply": "-",
"args": [
{
"id": "reclassify_uuid",
"apply": "reclassify",
"args": {
"breaks": {
"type": "class-breaks",
"id": "classes-1",
"boundaryType": "lessThan",
"classMap": [[3.3, 0], [5.0, 1], [6.1, 2]]
},
"raster": {
"id": "<ndvi_model_uuid>",
"display": "NDVI Time 0",
"tag": "1",
"apply": "/", "args": [
{ "apply": "-", "args": [
{
"type": "layer",
"label": "red",
"project": "123e4567-e89b-12d3-a456-426655440000",
"band": 3
},
{
"type": "layer",
"label": "nir",
"project": "123e4567-e89b-12d3-a456-426655440000",
"band": 4
}
]
},
{ "apply": "+", "args": [
{
"type": "layer",
"label": "red",
"project": "123e4567-e89b-12d3-a456-426655440000",
"band": 3
},
{
"type": "layer",
"label": "nir",
"project": "123e4567-e89b-12d3-a456-426655440000",
"band": 4
}
]}
]
}
}
},
{
"id": "reclassify_uuid",
"apply": "reclassify",
"args": {
"breaks": {
"type": "class-breaks",
"id": "classes-1",
"boundaryType": "lessThan",
"classMap": [[3.3, 0], [5.0, 1], [6.1, 2]]
},
"raster": {
"id": "<ndvi_model_uuid>",
"display": "NDVI Time 1",
"tag": "2",
"apply": "/", "args": [
{ "apply": "-", "args": [
{
"type": "layer",
"label": "red",
"project": "123e4567-e89b-12d3-a456-426655440001",
"band": 3
},
{
"type": "layer",
"label": "nir",
"project": "123e4567-e89b-12d3-a456-426655440001",
"band": 4
}
]
},
{ "apply": "+", "args": [
{
"type": "layer",
"label": "red",
"project": "123e4567-e89b-12d3-a456-426655440001",
"band": 3
},
{
"type": "layer",
"label": "nir",
"project": "123e4567-e89b-12d3-a456-426655440001",
"band": 4
}
]}
]
}
}
}
]
}
Alternative Parameter Usage
Above stated parameter expression is most flexible as it places full burden of parameter tracking on they who assemble and manipulate the expression tree, allowing for various strategies to do that.
However, it may be made more redaundant by using what we know about the parameter usage.
Given: A model template must specify all of its parameters and their types
- no other variable references are allowed in that sub-tree
- we can bind all the values at the root of that sub-tree
In this case binding is either:
- Bind a variable to a concrete value
- Bind a variable to a parameter above the tree
Bind to concrete
{
"name": "nir",
"type": "raster",
"mosaic": "123e4567-e89b-12d3-a456-426655440001",
"band": 4
}
Bind to another parameter
{
"name": "nir",
"type": "raster",
"ref": "LC8_0_nir"
}
In this form the model from the model library will have a params
elemnt that applies to it.
When the model template is inserted into some expression tree the params
element continues to exist at that level of the tree but now must be bound.
This form is more consistent with opaque models, where lacking the model body only parameters must be specified.
This form is not significantly harder to parse at evaluation time. The choice should be based on ease of manipulation for UI representation.
Dependency Re-Use
This yet part of the spec
Lets assume there is an operation that we must re-use and it is expensive. With definition above there is no explicit way to state the re-use. At most it is possible to duplicate an subtree.
An strucutre similar to PDAL pipeline can be useful here: http://www.pdal.io/pipeline.html The basic structure is to define stages with explicit inter-dependency. Resolving these dependencies would produce the full tree.
[
{
"apply": "costditance",
"args": { "..." },
"ref": "CD"
},
{
"apply": "-",
"args": [
{
"apply": "focal-avg",
"args": { "raster": "CD", "neighborhood": 4 }
},
{
"apply": "focal-slope",
"args": { "raster": "CD", "neighborhood": 6 }
}
]
}
]
Issue Analytics
- State:
- Created 7 years ago
- Comments:5 (5 by maintainers)
Top GitHub Comments
In case you missed it, this is the PR that implemented the JSON interpretation on the front-end in case it helps to see the adaptations necessary there.
Can this be closed? MAML lives.