Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement schemas #4

Closed
martinRenou opened this issue Jun 13, 2024 · 33 comments
Closed

Implement schemas #4

martinRenou opened this issue Jun 13, 2024 · 33 comments
Labels
enhancement New feature or request
Milestone

Comments

@martinRenou
Copy link
Member

We need to implement some schemas to get started.

A good starting point may be to implement schemas for TileLayer / GeoJSON layer.

@martinRenou martinRenou added the enhancement New feature or request label Jun 13, 2024
@martinRenou
Copy link
Member Author

martinRenou commented Jun 18, 2024

@davidbrochart @brichet

About our file format, I'm thinking we could do the following:

{
  "layers": {
    "1234-1234-1": {"type": "TileLayer", "name": "My Layer 1", "value": {"url": "https://...jpg"}},
    "1234-1234-2": {"type": "VectorLayer", "name": "My Layer 2", "value": {"source": "5678-5678-1", "styling": {"stroke": "red"}}}
  },
  "dataSources": {
    "5678-5678-1": {"type": "VectorSource", "value": {[GEOJSON-format]}}
  },
  "layerGroups": {
    "group1": {"layers": ["1234-1234-1", "..."], "visible": false},
    "group2": {"layers": ["1234-1234-1", "..."]},
  }
}

@davidbrochart
Copy link
Collaborator

Looks good.
It seems everything is indexed by UID except for the layer groups?
Should data sources be part of the file?

@martinRenou
Copy link
Member Author

martinRenou commented Jun 18, 2024

It seems everything is indexed by UID except for the layer groups

Right, we should probably indeed have UUIDs for groups too 👍🏽

Should data sources be part of the file?

Indeed it may be better to have data sources outside of the format.

An updated version of the above:

{
  "layers": {
    "1234-1234-1": {"type": "TileLayer", "name": "My Layer 1", "value": {"url": "https://...jpg"}},
    "1234-1234-2": {"type": "VectorLayer", "name": "My Layer 2", "value": {"source": "5678-5678-1", "styling": {"stroke": "red"}}}
  },
  "dataSources": {
    "5678-5678-1": {"type": "GeoJSONFile", "url": "./path/to/local.json"}
  },
  "layerGroups": {
    "9876-9876-1": {"name": "group1", "layers": ["1234-1234-1", "..."], "visible": false},
    "9876-9876-2": {"name": "group2", "layers": ["1234-1234-1", "..."]},
  }
}

@brichet
Copy link
Collaborator

brichet commented Jun 18, 2024

Yes, it looks good.
It may be better indeed to save the data sources outside. That would avoid duplication if a data file already exist, or if data comes from a URL.

@davidbrochart
Copy link
Collaborator

We should look into SpatiaLite.

@martinRenou
Copy link
Member Author

If we go closer to the maplibre design, our format could look like the following:

{
  "layers": {
    "1234-1234-1": {"type": "raster", "name": "My Layer 1", "source": "5678-5678-2"},
    "1234-1234-2": {"type": "line", "name": "My Layer 2", "source": "5678-5678-1", "styling": {"stroke": "red"}}
  },
  "sources": {
    "5678-5678-1": {"type": "geojson", "url": "./path/to/local.json"},
    "5678-5678-2": {"type": "raster", "url": "https://...jpg"},
  },
  "layerGroups": {
    "9876-9876-1": {"name": "group1", "layers": ["1234-1234-1", "..."], "visible": false},
    "9876-9876-2": {"name": "group2", "layers": ["1234-1234-1", "..."]},
  }
}

@martinRenou martinRenou added this to the 1.0.0 milestone Jun 19, 2024
@brichet
Copy link
Collaborator

brichet commented Jun 21, 2024

After discussion with @martinRenou, the groups may be the reference for the display.
This means that if some layers are not grouped, we should create a group of one element for each.

If we go in that direction, we should probably set the layerGroups as a list, to keep the groups order (and therefore the z-index).

{
  "layers": {
    "1234-1234-1": {"type": "raster", "name": "My Layer 1", "source": "5678-5678-2"},
    "1234-1234-2": {"type": "line", "name": "My Layer 2", "source": "5678-5678-1", "styling": {"stroke": "red"}}
  },
  "sources": {
    "5678-5678-1": {"type": "geojson", "url": "./path/to/local.json"},
    "5678-5678-2": {"type": "raster", "url": "https://...jpg"},
  },
  "layerGroups": [
   {"name": "group1", "layers": ["1234-1234-1", "..."], "visible": false},
   {"name": "group2", "layers": ["1234-1234-1", "..."]},
  ]
}

What do you think ?

@davidbrochart
Copy link
Collaborator

After discussion with @martinRenou, the groups may be the reference for the display.
This means that if some layers are not grouped, we should create a group of one element for each.

I'm not sure about that. This means that each time a layer is added, it is also added as a single layer group?

If we go in that direction, we should probably set the layerGroups as a list, to keep the groups order (and therefore the z-index).

Maybe we could keep the layerGroups entry as a map of ID to group, where a group has a z-index attribute?

@brichet
Copy link
Collaborator

brichet commented Jun 21, 2024

I'm not sure about that. This means that each time a layer is added, it is also added as a single layer group?

Yes, every layer should be in a group. Otherwise we would have to make distinction between the individual layers and the grouped ones.

Maybe we could keep the layerGroups entry as a map of ID to group, where a group has a z-index attribute?

That works too, I don't have a strong opinion on that.

@davidbrochart
Copy link
Collaborator

Yes, every layer should be in a group. Otherwise we would have to make distinction between the individual layers and the grouped ones.

But let's say a user creates a layer group with just one layer. We now have two layer groups with the same layer, and we need to make a distinction that one is an implicit layer group, and the other is an explicit layer group. I'm not sure it is better.

@martinRenou
Copy link
Member Author

We can make this transparent to the user so that the user does not know it's a group with one layer. If the user creates a group with one layer it will mostly be a no-op. It's really just for convenience for us on the display, so we just need to loop over the layer groups to meet every layer.

@martinRenou
Copy link
Member Author

It's like in the QGIS data structure, you have access to the layer tree which contains all layers, which is the equivalent to our list of layer groups.

@davidbrochart
Copy link
Collaborator

If the user creates a group with one layer it will mostly be a no-op.

What do you mean by "no-op"? It's a valid group, for instance a user could mark the layer as not visible in this group, or with a special transparency value.

It's like in the QGIS data structure, you have access to the layer tree which contains all layers, which is the equivalent to our list of layer groups.

I think that if we go that road we would need a layer group attribute that would flag this group as implicit, e.g. "is_layer": true. And these implicit layer groups would not be saved to the file, but rather created at runtime. For me it's really a convenience for the UI to treat every layer as a layer group for simplicity.

@martinRenou
Copy link
Member Author

martinRenou commented Jun 21, 2024

How about this instead:

{
  "layers": {
    "1234-1234-1": {"type": "raster", "name": "My Layer 1", "source": "5678-5678-2"},
    "1234-1234-2": {"type": "line", "name": "My Layer 2", "source": "5678-5678-1", "styling": {"stroke": "red"}},
    "..."
  },
  "sources": {
    "5678-5678-1": {"type": "geojson", "url": "./path/to/local.json"},
    "5678-5678-2": {"type": "raster", "url": "https://...jpg"},
  },
  "layersTree": [
       "1234-1234-1",
       {
           "name": "group1", "layers": [
               "1234-1234-2", 
               {"name": "group11", "layers": ["1234-1234-11", "..."]}
            ], 
            "visible": false
       },
       {"name": "group2", "layers": ["1234-1234-3", "..."]},
   ]
}
  • no implicit single layer group
  • we can create groups in the tree
  • all layers are included in the tree and we can loop over the tree to discover all layers

@brichet
Copy link
Collaborator

brichet commented Jun 21, 2024

And these implicit layer groups would not be saved to the file, but rather created at runtime

If I understand correctly, we would lost the z-index with that, or should have a z-index on the layer itself, which seems a bit confusing (mixing z-index in groups and layers).

@martinRenou
Copy link
Member Author

Yeah I think the z-index is just implicit with the tree/groups structure

@davidbrochart
Copy link
Collaborator

I don't quite understand the notion of layer tree.

@martinRenou
Copy link
Member Author

What don't you understand?

@brichet
Copy link
Collaborator

brichet commented Jun 21, 2024

How about this instead:

Looks great, we assume that if the entry is a string it is an individual layer, otherwise it is a group. And the layer tree is a list in this schema.

I don't quite understand the notion of layer tree.

The idea is to be able to hide or perform other action on a group of layers.

@davidbrochart
Copy link
Collaborator

So in a layer tree a node can be a layer or a layer group, and a layer group has layers that can again be layer groups?
That's very confusing. Why aren't these layers a flat list of layers?

@brichet
Copy link
Collaborator

brichet commented Jun 21, 2024

Why aren't these layers a flat list of layers?

To allow creating groups. We can think about a scenario using a map and some layers in background, and have several groups displaying data that are not meant to be displayed together. Using groups, you can easily switch between the different data you want to display.

@martinRenou
Copy link
Member Author

martinRenou commented Jun 21, 2024

QGIS and felt both have the ability to group layers, I'm not sure how felt is structuring its data this but QGIS has a tree of layers in memory and saved into the file.

In felt you can create groups to control visibility or other visual parameters to a set of layers, and to structure a bit more your file:

Screencast.from.2024-06-21.11-23-50.mp4

A flat list of layers can quickly be unreadable if you have many layers, so being able to group them sounds like a basic feature to me

@davidbrochart
Copy link
Collaborator

I know groups are useful, what I mean is that groups should just consist of a flat list of layers, but not a possibly nested list of layers and groups.

@martinRenou
Copy link
Member Author

Having nested groups would be useful to users too, and it would not bring to much complication for us. So why not do it?

@davidbrochart
Copy link
Collaborator

What's the use case, and how do you display nested groups?

@martinRenou
Copy link
Member Author

It looks like felt does not support nested groups, but QGIS does:

Screenshot from 2024-06-21 11-33-31

What's the use case

Well:

  • Backround Data group 1
    • Streets and Buildings subgroup
      • Street lines
      • Building polygons
      • Historical buildings
    • Ocean
    • Whatever
  • Precipitation Forecast

display nested groups

I'd expect that reading through the tree would give us a natural z-index?

@martinRenou
Copy link
Member Author

QGIS does

I think that's an important point, if we want to support QGIS files

@davidbrochart
Copy link
Collaborator

OK, makes sense. If the UUIDs in the layer tree can be layers or layer groups, let's make sure we don't end up with recursion 😄

@martinRenou
Copy link
Member Author

Yeah.. We need to make sure the GUI never creates such broken file, but we cannot stop people from manually editing the file and making mistakes. Proper error handling is all we can do.

@brichet
Copy link
Collaborator

brichet commented Jun 21, 2024

I'm trying to build the nested schema but this creates an expected object.

IIUC we have:

  • the LayerTree, a list of string or LayerGroup
  • the LayerGroup, an object with (at least) name and layer (which should be a LayerTree)

The tree and group calls each others.

So I added the layerTree in the content:

"layerTree": {
  "$ref": "#/definitions/jGISLayerTree"
},

and in the definitions, I added:

"jGISLayerGroup": {
  "title": "IJGISLayerGroup",
  "type": "object",
  "additionalProperties": false,
  "required": ["name", "layers"],
  "properties": {
    "name": {
      "type": "string"
    },
    "layers": {
      "$ref": "#/definitions/jGISLayerTree"
    },
    "visible": {
      "type": "boolean"
    },
    "parameters": {
      "type": "object"
    }
  }
},
"jGISLayerTree": {
  "title": "IJGISLayerTree",
  "type": "array",
  "default": [],
  "items": {
    "oneOf": [
        {"type": "string"},
        {
          "type": "object",
          "$ref": "#/definitions/jGISLayerGroup"
        }
    ]
  }
}

When building it, I have an additional IJGISLayerGroup1, probably because of recursion.

export type IJGISLayerTree = (string | IJGISLayerGroup)[];
export interface IJGISLayerGroup {
  name: string;
  layers: IJGISLayerTree;
  visible?: boolean;
  parameters?: {
    [k: string]: any;
  };
}
export interface IJGISLayerGroup1 {
  name: string;
  layers: IJGISLayerTree;
  visible?: boolean;
  parameters?: {
    [k: string]: any;
  };
}

Don't know how to properly declare it.

@davidbrochart
Copy link
Collaborator

Maybe there should only be LayerGroup, an object with name and layers, which should be a list of strings (individual layer UUID) or layer groups. And there would be an implicit root layer group (maybe with an empty name).

@brichet
Copy link
Collaborator

brichet commented Jun 21, 2024

Maybe there should only be LayerGroup, an object with name and layers, which should be a list of strings (individual layer UUID) or layer groups. And there would be an implicit root layer group (maybe with an empty name).

Looks good, thanks

@brichet brichet mentioned this issue Jun 26, 2024
3 tasks
@martinRenou
Copy link
Member Author

Closing as the basic schemas have been implemented, we can iterate

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants