Maps4HTML / geoserver

Maps for HTML MapML Extension / plug-in development fork of GeoServer
https://docs.geoserver.org/latest/en/user/extensions/mapml/index.html
Other
3 stars 1 forks source link

Enable multi-layer GetMap, GetFeatureInfo to return text/mapml structured to request multi-layer vector tiles #72

Open prushforth opened 4 months ago

prushforth commented 4 months ago

Currently with the GeoServer MapML extension, there is no possibility of generating a text/mapml response containing a single-extent, multi-layer request template i.e. a \. For one thing, this is because each layer has its own "Use Features" setting that is not driven by a WMS parameter. What's more, it was unclear how to serialize layers in such a request that are not "vector-capable" such as image layers.

If the GeoServer MapML extension were modified to examine the request mapmlusefeatures:true|false token, it would be possible for a request for text/mapml to generate a single \ containing a \ (wherein the tref attribute contains a WMS GetMap URL template containing the mapmlfeatures:true format_options parameter token and also contains a multi-layer layers= parameter list value).

This requirement is to establish the behaviour of the GeoServer MapML extension when it processes a multi-layer GetMap request when the format_options parameter token mapmlmultiextent is set to false.

MapML document types available from GeoServer form a conceptual tree structure, in which the nodes are document types and the edges are links, either complete URLs or templated via the MapML \ URL template. The root node in the tree is the HTML preview document, which contains a link to a GeoServer layer in text/mapml format. GeoServer's Layer and Layer Group settings pages affect the how the HTML preview document root node is configured and serialized. In particular, the "Use Tiles", "Use Features" and "Multiple Extent Elements" settings for the Layer and Layer Group editing pages will dictate the values format_options= parameter tokens that are serialized into the HTML preview in the GetMap URL in the <layer- src="..."></layer-> src URL. Screenshot 2024-06-06 114742-resized

Inner node parameters

The middle, or internal node in the above diagram is the first level of text/mapml document available from GeoServer, linked to by the \ src URL in the root node, and its configuration, or state, is controled by three tokens for the format_options GeoServer 'vendor' parameter. The tokens' values control, and only apply to the internal node (middle tier from diagram above) state:

(The default value, or the value if the token is omitted, is false)

Table describing different combinations of mapmlmultiextent, mapmlusefeatures, and mapmlusetiles

  mapmlmultiextent  
FALSE TRUE
mapmlusefeatures FALSE create singleton map-extent
traditional WMS image client
rel=image
for each layer in request
-> expand / de-nest layer groups in order
for each layer:
-> create a map-extent for layer
traditional WMS image client
rel=image
FALSE mapmlusetiles
create singleton map-extent
if no tile cache
-> use tile-shaped WMS GetMap for image format
-> rel=image
-> rel=query uses WMS GetFeatureInfo
else
-> use GetTile URL template rel=tile
-> rel=query WMTS GetFeatureInfo for text/mapml infoformat
for each layer in request
-> expand / de-nest layer groups in order
for each layer:
if no tile cache
-> use tile-shaped WMS GetMap for image format
-> rel=image
-> rel=query uses WMS GetFeatureInfo
else
-> use GetTile URL template rel=tile
-> rel=query WMTS GetFeatureInfo for text/mapml infoformat
TRUE
TRUE create singleton map-extent
traditional WMS image client
URL template rel=features type=text/mapml
query link if queryable
for each layer in request
-> expand / de-nest layer groups in order
for each layer:
-> create a map-extent for layer
if layer is raster
-> query link
-> traditional WMS image client
-> rel=image
else
-> rel=features type=text/mapml
-> no query link (features)
-> URL template contains mapmlfeatures:true
FALSE mapmlusetiles
create singleton map-extent
if no tile cache
-> use tile-shaped GetMap as URL template
-> rel=tile type=text/mapml
-> URL template contains mapmlfeatures:true
-> generate rel=query map-link over WMS GetFeatureInfo, infoformat=text/mapml
else
-> use GetTile for text/mapml
-> generate rel=query map-link over WMTS GetFeatureInfo, infoformat=text/mapml
for each layer in request
-> expand / de-nest layer groups in order
for each layer:
-> create a map-extent for layer
if layer is raster
-> if no tile cache
-> -> use tile-shaped GetMap rel=tile, format=image/*
-> else
->-> use GetTile, WMTS GetFeatureInfo for image type
-> -> query link WMTS GetFeatureInfo if layer is queryable
else
-> if no tile cache
-> -> use tile-shaped GetMap rel=tile type=text/mapml
-> -> URL template contains mapmlfeatures:true
-> else
-> -> use GetTile, WMTS GetFeatureInfo for text/mapml
-> -> query link WMTS GetFeatureInfo if layer is queryable
TRUE

mapmlmultiextent:false | true

mapmlmultiextent:false | true - controls the number and state of \ elements generated in the document, depending on the layers referenced by the layers= WMS parameter. If false (the default), a singleton \ element will be serialized, and that map-extent will be populated with child \ and associated \ elements that conform to the mapmlusefeatures:false | true and mapmlusetiles:false | true values. In the following listing, the document was generated with neither parameter, so they default to false.

Inner node created according to mapmlmultiextent:false, mapmlusefeatures:false, mapmlustiles:false parameter

```html Spearfish ```

In the following listing of a single layer (LayerGroup) request, the document was generated with the inner node parameters mapmlusefeatures:false | true and mapmlusetiles:false | true values set to true. Note that the fact that the configuration uses the "GetTile" WMTS request implies that a tile cache with an appropriate GridSet has been set up by the administrator. Had no tile cache been previously configured, the generated \ would use "tile-shaped" GetMap request templates and associated \s.

Inner node created according to mapmlmultiextent:false; mapmlusefeatures:true; mapmlusetiles:true, tile cache previously established

```html Spearfish ```

For a format_options=mapmlmultiextent:true value passed to the inner node generator, if the layers parameter is list-valued, that is, references more than a single layer in a comma-separated list OR the layers parameter names a single GeoServer LayerGroup, the resulting text/mapml inner node document will contain one \ element per layer in the layers list OR per layer in the LayerGroup, with the \s serialized according to the values of the mapmlusefeatures and mapmlusetiles format_options values. In the following listing, both of those options' values have defaulted (to false), so each generated \ is configured to be a "traditional" WMS GetMap / GetFeature client:

Inner node created according to mapmlmultiextent:true, mapmlusefeatures:false, mapmlusetiles:false

```html Spearfish layer group layers ```

Inner node created according to mapmlmultiextent:true; mapmlusefeatures:true, mapmlusetiles:false

```html Spearfish, format_options=mapmlmultiextent:true; mapmlusefeatures:true; mapmlusetiles:false .bbox {display:none} .simple_streams-r1-s1{stroke-opacity:1.0; stroke-dashoffset:0; stroke-width:2.0; stroke:#003EBA; stroke-linecap:butt} .simple_roads-r1-s1{stroke-opacity:1.0; stroke-dashoffset:0; stroke-width:2.0; stroke:#AA3333; stroke-linecap:butt} .restricted-r1-s1{stroke-opacity:1.0; stroke-dashoffset:0; stroke-width:1.0; fill:#FF0000; fill-opacity:0.7; stroke:#AA0000; stroke-linecap:butt} .capitals-r1-s1{r:48.0; stroke-opacity:1.0; stroke-dashoffset:0; well-known-name:circle; stroke-width:2.0; opacity:1.0; fill:#000000; fill-opacity:1.0; stroke:#000000; stroke-linecap:butt} .point-r1-s1{r:48.0; well-known-name:square; opacity:1.0; fill:#FF0000; fill-opacity:1.0} ```

Inner node created according to mapmlmultiextent:true; mapmlusefeatures:true; mapmlusetiles:true

```html Spearfish elevation,Spearfish streams,Spearfish roads,Spearfish restricted areas,Spearfish bug locations,Spearfish archeological sites .bbox {display:none} .simple_streams-r1-s1{stroke-opacity:1.0; stroke-dashoffset:0; stroke-width:2.0; stroke:#003EBA; stroke-linecap:butt} .simple_roads-r1-s1{stroke-opacity:1.0; stroke-dashoffset:0; stroke-width:2.0; stroke:#AA3333; stroke-linecap:butt} .restricted-r1-s1{stroke-opacity:1.0; stroke-dashoffset:0; stroke-width:1.0; fill:#FF0000; fill-opacity:0.7; stroke:#AA0000; stroke-linecap:butt} .capitals-r1-s1{r:48.0; stroke-opacity:1.0; stroke-dashoffset:0; well-known-name:circle; stroke-width:2.0; opacity:1.0; fill:#000000; fill-opacity:1.0; stroke:#000000; stroke-linecap:butt} .point-r1-s1{r:48.0; well-known-name:square; opacity:1.0; fill:#FF0000; fill-opacity:1.0} ```

Leaf node parameters

mapmlfeatures:false | true

The mapmlfeatures format_options token value drives the creation of the leaf node document type. The middle tier node in the diagram will generate templated links embedded in \s in the document. The \ URL template to the leaf node text/mapml document will accordingly sometimes contain the mapmlfeatures:true token value, which will cause GeoServer to serialize the layer and layer group content as MapML feature and/or image tile data according to the individual layer's underlying data type.

mapmlfeatures:false | true

An example leaf node document is:

<mapml- xmlns="...">
  <map-head>
   <map-title>Layer Group ZZZ Example of interleaved features and tiles</map-title>
  </map-head>
  <map-body>
    <map-feature>
      <map-featurecaption>A feature from Layer A</map-featurecaption>
      <map-geometry><map-point><map-coordinates>-72 43.5</map-coordinates></map-point></map-geometry>
    </map-feature>
   <!-- Layer B is imagery must be represented by the tile that covers the bbox of the GetMap -->
    <map-tile row="13" col="13" zoom="3" src="tile-shaped non-templated GetMap resource URL"></map-tile>
   <map-feature>
      <map-featurecaption>A feature from Layer C</map-featurecaption>
      <map-geometry><map-linestring><map-coordinates>...</map-coordinates></map-linestring></map-geometry>
   </map-feature
  </map-body>
</mapml->
aaime commented 1 month ago

Just a clarification for the case "mapmlmultiextent: false; mapmlusefeatures:true; mapmlusetiles:true", specifically for the situation where multiple layers have been requested (not a single layer group). In this case, one cannot use WMTS, even if each single layer is cached, because the protocol only allows one tile at a time, one cannot ask for a list of layers. This forces falling back to tiled WMS.

However, if the request is a group, and the group is tile cached, then yes, it can be handled as a single WMTS request instead.

prushforth commented 1 month ago

However, if the request is a group, and the group is tile cached, then yes, it can be handled as a single WMTS request instead.

Say a request comes in as follows: request=GetMap&format_options=mapmlmultiextent: false; mapmlusefeatures:true; mapmlusetiles:true&layers=a,b,c (your scenario above, basically)

where b is a layer group. I believe that I've specified that b should be de-nested and rendered as features within the one tile in order in the a,b,c stack, just as it would be if it was being rendered to a bitmap in a traditional WMS request (that was being cached as a tile).

What I wrote above for this situation is:

create singleton map-extent if no tile cache -> use tile-shaped GetMap as URL template -> rel=tile type=text/mapml -> URL template contains mapmlfeatures:true -> generate rel=query map-link over WMS GetFeatureInfo, infoformat=text/mapml else -> use GetTile for text/mapml -> generate rel=query map-link over WMTS GetFeatureInfo, infoformat=text/mapml

The map-extent would be generated to create 'tile-shaped GetMap requests'

<map-link rel=tile type=text/mapml tref="...GetMap&layers=a,b,c&format_options:mapmlfeatures:true...">

but I see that there is an 'else' above that says to use GetTile and I believe that is your concern. I think I could clarify that with further logic to clarify that using GetTile is only possible in the single-layer case. Am I correct, that's what you're saying?