Maps4HTML / MapML.js

A custom <mapml-viewer> and <layer-> element suite
https://maps4html.org/MapML.js/
Other
57 stars 16 forks source link

Links (and spans) in <geometry> content #367

Closed prushforth closed 3 years ago

prushforth commented 3 years ago

An odd way to write a spec, but here are a few ideas, for discussion. Please comment @shepazu and @Malvoz !

<mapml lang="en" xmlns="http://www.w3.org/1999/xhtml/">
    <head>
        <meta charset="UTF-8" />
        <title>BasicPolygons</title>
        <meta content="text/mapml" http-equiv="Content-Type" />
        <meta name="extent" content="top-left-longitude=-2.000000,top-left-latitude=6.000000,bottom-right-longitude=2.000000,bottom-right-latitude=-1.000000" />
        <meta name="cs" content="gcrs" />
        <meta name="projection" content="MapML:EPSG:4326" />
    </head>
    <body>
        <!-- links here would make no sense -->
        <feature id="BasicPolygons.1" class="BasicPolygons">
        <!-- links here would make no sense -->
        <geometry>

        <!-- links here would be drawn around the entire geometry. Each polygon in a multipolygon would
                be interactive; same for any other geometry type. The holes in polygons would be non-interactive; 
                if there was another feature in
               the hole (an island, say), the island could itself be wrapped in a link, so we would want the
               island's link's behaviour to take over. A span here might not be too useful, but would not be
            harmful, I think; you already have the root geometry element to hang attributes on (e.g. class, and so on).
        FWIW I believe that some markup is not supposed to be found in the content of <a> (see Permitted Content): 
            https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#properties -->

            <!-- Regarding behaviour of links, I think we should use the existing "target" attribute vocabulary:
            https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#attr-target
            and interpret that value as follows for maps:

            IFF the linked resource mime type is text/mapml:

                _self: replace the current layer (i.e. replace the current map layer; same behaviour as named style links) (Default)
                _blank: usually, add a new layer on top of map, but users can configure browsers to open a new window instead.
                _parent: replace all layers in the map with the linked layer. If no other layers, behaves as _self.
                _top: the topmost browsing context (the page) should navigate to the map resource, synthesize an HTML document
                around it, and load the map resource into the synthesized map and process any URL fragments to control the
                initial location of the map view. If no fragment, set zoom to lowest value, centered on extent of map doc.
                (the "highest" context that’s an ancestor of the current one). 
                If no ancestors, behaves as _self.  

            ELSE 
                do what browsers do with similarly tagged links of today's Web.

        -->

            <a href="https://example.org">
                <span class="harmlessButNotUseful">
                <multipolygon>
                    <!-- a link here would be drawn around the the entity or entities which it wrapped.  Say there were
                    three polygons in this multipolygon; a link could wrap two of the three. Similarly,
                    say you wanted to style two of the three in a different way than the third. You should
                    be able to wrap selected ones in a <span class="twoOfThreeStyle"> - that would 
                    prevent you from having to put duplicate spans to get the styles you want. -->

                    <polygon>
                        <!-- links in coordinates would be processed like spans are, except that
                        on output they are styled and behave as links.  Same deal, just copy them and
                    their attributes to the appropriate output. -->
                        <coordinates>-1.0 0.0 <a href="./door">0.0 1.0 1.0 0.0</a> 0.0 -1.0 -1.0 0.0</coordinates>
                    </polygon>
                </multipolygon>
              </span>
            </a>
            </geometry>
            <properties>
                <table>
                    <thead>
                        <tr>
                            <th role="columnheader" scope="col">Property name</th>
                            <th role="columnheader" scope="col">Property value</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <th scope="row">ID</th>
                            <td itemprop="ID" />
                        </tr>
                    </tbody>
                </table>
            </properties>
        </feature>
    </body>
</mapml>
ahmadayubi commented 3 years ago

So if a feature is a link then the popup is no longer useful in those cases as clicking on the feature now 'goes to' the link instead of opening a popup?

prushforth commented 3 years ago

I guess that's true, but putting a link over any text in a browser takes priority over other behaviour you might give it.

ahmadayubi commented 3 years ago

I guess that's true, but putting a link over any text in a browser takes priority over other behaviour you might give it.

Yeah that seems reasonable, just confirming the interaction.

prushforth commented 3 years ago

I think it might be reasonable to specify the input AND the output here, as I believe you suggested, @ahmadayubi. For example a polygon with a hole:

<polygon>
    <coordinates>6.0E-4 -0.0018 0.001 -6.0E-4 0.0024 -1.0E-4 0.0031 -0.0015 6.0E-4 -0.0018</coordinates>
    <coordinates>0.0017 -0.0011 0.0025 -0.0011 0.0025 -6.0E-4 0.0017 -6.0E-4 0.0017 -0.0011</coordinates>
</polygon>

would normally only need to generate a single to stroke and fill the polygon: e.g.

<g id="polygon">
  <path d="M 35 -10 L 45 -45 15 -40 10 -20 Z M 20 -30 L 35 -35 30 -20 Z" fill="..." stroke="..." fill-rule="nonzero" />
 </g>

Aside: I believe OGC Simple Features polygons should always be filled with the fill-rule of "nonzero" (that's one of the reasons they're "simple", I believe).

However, if there was a span in or around one of the rings, as you find with tile boundaries, you might want to generate separate <path>s appropriate to the situation, which I'll work on tomorrow.

ahmadayubi commented 3 years ago

However, if there was a span in or around one of the rings, as you find with tile boundaries, you might want to generate separate s appropriate to the situation, which I'll work on tomorrow.

It already does that I believe.

ahmadayubi commented 3 years ago

I believe OGC Simple Features polygons should always be filled with the fill-rule of "nonzero" (that's one of the reasons they're "simple", I believe).

Interesting that simple features specifies an SVG full rule. How does nonzero affect rendering compared to leaflets use of evenodd in terms of visual output @prushforth?

prushforth commented 3 years ago

Give this test case a try. FWIW I think we can drop the role=region, as it doesn't seem appropriate. We can add spans/links in a second iteration; when geometries have spans in them, there will need to be duplication of paths, and the order matters: the spanned content should come after the base polygon.

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>index-map.html</title>
    <script type="module" src="dist/mapml-viewer.js"></script>
     <style> 
       html, 
       body { 
         height: 100%; 
       } 
       * { 
         margin: 0; 
         padding: 0; 
       } 

       /* Specifying the `:defined` selector is recommended to style the map 
       element, such that styles don't apply when fallback content is in use 
       (e.g. when scripting is disabled or when custom/built-in elements isn't 
       supported in the browser). */ 
       mapml-viewer:defined { 
         /* Responsive map. */ 
          max-width: 100%;  

         /* Full viewport. */ 
          width: 800px;  
          height: 600px;  

         /* Remove default (native-like) border. */ 
         /* border: none; */ 
       } 

       /* Pre-style to avoid FOUC of inline layer- and fallback content. */ 
       mapml-viewer:not(:defined) > * { 
         display: none; 
       } 
       /* Ensure inline layer content is hidden if custom/built-in elements isn't 
       supported, or if javascript is disabled. This needs to be defined separately 
       from the above, because the `:not(:defined)` selector invalidates the entire 
       declaration in browsers that do not support it. */ 
       layer- { 
         display: none; 
       } 
     </style> 
     <noscript> 
       <style> 
         /* Ensure fallback content (children of the map element) is displayed if 
         custom/built-in elements is supported but javascript is disabled. */ 
         mapml-viewer:not(:defined) > :not(layer-) { 
           display: initial; 
         } 
       </style> 
     </noscript> 
  </head>
  <body>
    <mapml-viewer projection="OSMTILE" zoom="16" lat="0" lon="0" controls>
      <layer- label="LAKES" checked>
        <meta name="cs" content="pcrs" >
        <meta name="projection" content="OSMTILE" >
        <meta name="extent" content="top-left-easting=66.79,top-left-northing=-11.13,bottom-right-easting=345.09,bottom-right-northing=-200.38" >
        <style>
          .poly {
            fill: red;
            stroke: pink;
            stroke-width: 3px;
          }
          .multipoly {
            fill: blue;
          stroke: cyan;
          stroke-width: 3px;
          }
          .geocollection {
            fill: green;
          stroke: chartreuse;
          stroke-width: 3px;
          }
        </style>
        <feature id="polygon.1">
          <featurecaption>Polygon Feature</featurecaption>
            <geometry>
                    <polygon class="poly">
                        <coordinates>66.79169447596414 -200.37508346187343 111.31949079327357 -66.79169447822557 267.16677790385654 -11.131949079964679 345.09042145914805 -166.97923620971676 66.79169447596414 -200.37508346187343</coordinates>
                        <coordinates>189.24313434856506 -122.45143988021553 278.29872698318394 -122.45143988021553 278.29872698318394 -66.79169447822557 189.24313434856506 -66.79169447822557 189.24313434856506 -122.45143988021553</coordinates>
                    </polygon>
            </geometry>
        </feature>
<!-- The above feature should render as:
       <g aria-expanded="false" aria-label="Polygon Feature" data-fid="polygon.1" tabindex="0" class="leaflet-interactive poly">
           I would not object to there being an intermediate group here, but it's not necessary 
           if there are spans in the polygon, the markup will require some more paths 
          <path data-fid="polygon.1" d="M428 384L447 328L512 305L544 370L428 384zM479 351L517 351L517 328L479 328L479 351z"></path>
        </g>
        -->
        <feature id="multipolygon.2">
          <featurecaption>MultiPolygon Feature</featurecaption>
            <geometry>
              <multipolygon>
                    <polygon class="multipoly">
                        <coordinates>366.79169447596414 -200.37508346187343 411.31949079327357 -66.79169447822557 567.16677790385654 -11.131949079964679 645.09042145914805 -166.97923620971676 366.79169447596414 -200.37508346187343</coordinates>
                        <coordinates>489.24313434856506 -122.45143988021553 578.29872698318394 -122.45143988021553 578.29872698318394 -66.79169447822557 489.24313434856506 -66.79169447822557 489.24313434856506 -122.45143988021553</coordinates>
                    </polygon>
              </multipolygon>
            </geometry>
        </feature>
<!-- The above should render as:
      <g aria-expanded="false" aria-label="MultiPolygon Feature" data-fid="multipolygon.2" tabindex="0" class="leaflet-interactive ">
        <g class="multipoly"> need a group for each polygon in the multipolygon
         if there are spans in the polygon, the markup will require some more paths 
          <path d="M554 384L572 328L637 305L670 370L554 384M605 351L642 351L642 328L605 328L605 351"></path>
        </g>
      </g>
    -->
        <feature id="geometrycollection.3">
          <featurecaption>GeometryCollection Feature</featurecaption>
            <geometry>
              <geometrycollection>
                    <polygon class="geocollection">
                        <coordinates>66.79169447596414 -0.37508346187343 111.31949079327357 133.20830552177443 267.16677790385654 188.868050920035321 345.09042145914805 33.02076379028324 66.79169447596414 -0.37508346187343</coordinates>
                        <coordinates>189.24313434856506 77.54856011978447 278.29872698318394 77.54856011978447 278.29872698318394 133.20830552177443 189.24313434856506 133.20830552177443 189.24313434856506 77.54856011978447</coordinates>
                    </polygon>
              </geometrycollection>
            </geometry>
        </feature>
<!--        Should render to:
  <g aria-expanded="false" aria-label="GeometryCollection Feature" data-fid="geometrycollection.3" tabindex="0" class="leaflet-interactive">
    <g class="geocollection"> if there are spans in the polygon, the markup will require some more paths 
        <path d="M428 300L447 244L512 221L544 286L428 300M479 268L517 268L517 244L479 244L479 268"></path>
    </g>
  </g>
        -->
      </layer->
    </mapml-viewer>
  </body>
</html>
Malvoz commented 3 years ago
IFF the linked resource mime type is text/mapml:

                _self: replace the current layer (i.e. replace the current map layer; same behaviour as named style links) (Default)
                _blank: usually, add a new layer on top of map, but users can configure browsers to open a new window instead.
                _parent: replace all layers in the map with the linked layer. If no other layers, behaves as _self.
                _top: the topmost browsing context (the page) should navigate to the map resource, synthesize an HTML document
                around it, and load the map resource into the synthesized map and process any URL fragments to control the
                initial location of the map view. If no fragment, set zoom to lowest value, centered on extent of map doc.
                (the "highest" context that’s an ancestor of the current one). 
                If no ancestors, behaves as _self.  

I believe this suggests that each layer is a browsing context container (which may be a prerequisite to enable the sandboxing model as suggested by Amelia in https://github.com/Maps4HTML/MapML/issues/22#issuecomment-421880081), but that's not defined for the layer element in the spec.

<coordinates>-1.0 0.0 <a href="./door">0.0 1.0 1.0 0.0</a> 0.0 -1.0 -1.0 0.0</coordinates>

Is the author supposed to give that link an accessible name? Otherwise it is "0.0 1.0 1.0 0.0". I think coordinates not being attribute values (which was questioned by Simon Pieters and answered by Peter in https://github.com/Maps4HTML/MapML/issues/70) can generally be problematic for assistive technology (any AT that does not understand MapML would announce all contents of <coordinates>, so that's at least an issue in regards to backwards compatibility).

Malvoz commented 3 years ago

FWIW the related UCR Capability of this issue is https://maps4html.org/HTML-Map-Element-UseCases-Requirements/#capability-hyperlinks (yet to be discussed).

prushforth commented 3 years ago

I believe this suggests that each layer is a browsing context container

I think so, and I think that's appropriate, to be discussed.

Is the author supposed to give that link an accessible name? Otherwise it is "0.0 1.0 1.0 0.0".

Good point, I guess the accessible value of a similar link in human-readable text would be the text content of the link. So yes the author would have to give it an accessible name, but I think a screen reader could say something like "Line of length (whatever)" or something like that instead of reading out the coordinate.

Backwards compatibility is worth more discussion!

prushforth commented 3 years ago

Thanks to @Malvoz, who remembered that this feature is under consideration as a UCR. There has not been much discussion over there, but we'll hold polyfill-specific discussion here.