Esri / geoportal-server-catalog

Esri Geoportal Server is a next generation open-source metadata catalog and editor, based on elasticsearch.
https://www.esri.com/en-us/arcgis/products/geoportal-server/overview
Apache License 2.0
100 stars 62 forks source link

The CSW client Add to map button requires dc:type=liveData or downloadableData to be set #397

Closed lindhor closed 3 years ago

lindhor commented 3 years ago

In https://github.com/Esri/geoportal-server-catalog/blob/master/components/CswClient/Pro/CswClient/Common/CswProfile.cs#L351 it is a hardcoded check that only sets record.IsLiveDataOrMap = true and thus enables the Add to map button if dc:type equals liveData or downloadableData.

Many CSW servers do not use these properties even if they have services that are meant to be possible to add as layers in ArcGIS Pro.

A suggestion would be to add a comma separated list of custom "live" type values in the config file CswClient.properties and let the two current values be specified by default.

It might also be worth considering changing the logic a bit so that the button is added for servers that do not support type queries according to their profile (SupportContentTypeQuery=False in CSWProfiles.xml). i.e. something like:

                    XmlNode node = xmlnode.SelectSingleNode("Type");
                    if(filter_livedatamap) {
                      if (node != null)
                      {
                          /* CSW Server supports type queries and has type specified */
                          record.IsLiveDataOrMap = node.InnerText.Equals("liveData", StringComparison.OrdinalIgnoreCase);
                          /* TODO make the list of values used to detect live data configurable */
                          if (!record.IsLiveDataOrMap)
                          {
                              /* CSW Server supports type queries and has type other than liveData specified */
                              record.IsLiveDataOrMap = node.InnerText.Equals("downloadableData", StringComparison.OrdinalIgnoreCase);
                          }
                      }
                      else
                      {
                          /* CSW server supports type queries but this service does not include a type parameter */
                          record.IsLiveDataOrMap = false;
                      }
                    }
                    else
                    {
                        /* CSW server does not support type queries, default to live in order to enable Ad to map buton */
                        record.IsLiveDataOrMap = true;
                    }

Or make this behaviour configurable as well, i.e. AllowAddToMapButtonForAllServices=true/false in CswClient.properties or as a new optional tag in the CSWProfiles.xml if you want to set this per profile.

Note that the *_GetRecords_Request.xslt files are configurable to the extent of which records are listed based on for example dc:type equals liveData below, but that does not affect the Add to map button, since that decision is based on the metadata in the returned record.

    <!-- LiveDataOrMaps search -->
    <xsl:template match="/GetRecords/LiveDataMap" xmlns:ogc="http://www.opengis.net/ogc">
        <xsl:if test="translate(normalize-space(./text()),'true', 'TRUE') ='TRUE'">
            <ogc:PropertyIsEqualTo>
                <ogc:PropertyName>dc:type</ogc:PropertyName>
                <ogc:Literal>liveData</ogc:Literal>
            </ogc:PropertyIsEqualTo>
        </xsl:if>
    </xsl:template>
mhogeweg commented 3 years ago

This is an age-old problem. Many links that appear in metadata aren't in fact map service links but for example thumbnails, a getmap request (for WMS for example), web pages (for data download), etc. Metadata allows for multiple links, but only one 'type'.

I agree that a more flexible handling of the links would be useful, but perhaps one that doesn't depend on a metadata element (that cannot be trusted anyway), but instead interrogates the links in the metadata and figures out which one(s) can be consumed in ArcGIS Pro for example.

For example, a layer file (.lyrx) can be opened from a URL into ArcGIS Pro, but is it 'LiveData'? or 'downloadable data'?

lindhor commented 3 years ago

Thanks for your comment! You are probably right but I'm not sure how we could evaluate the metadata links to understand what type of content they refer to. Do you have any ideas?

For my purposes it is currently only OGC services I'm looking into providing support for. For that purpose I think the value of ./gmd:distributionInfo/gmd:MD_Distribution/gmd:transferOptions/gmd:MD_DigitalTransferOptions/gmd:onLine/gmd:CI_OnlineResource/gmd:linkage/gmd:URL (providing a value like https://mapserver:443/MapService/WMS.axd/vmap0) would be enough - if I could find a stable way to understand that it is a OGC service of some kind. Looking into the response from our server the only metadata I see can be used (apart from guessing based on the URL by Store ring matching) is ./gmd:dataQualityInfo/md:DQ_DataQuality/gmd:report/gmd:DQ_DomainConsistency/gmd:result/gmd:DQ_ConformanceResult/gmd:specification/gmd:CI_Citation/md:identifier/gmd:MD_Identifier/gmd:code/gco:CharacterString, with values like urn:ogc:serviceType:WebMapService:1.3.0. I don't think it's possible to count on this to exist on all servers however.

Also, if we implement something like this, would you want to configure it per catalog, per profile or central for all? I think per catalog or profile might make sense.

mhogeweg commented 3 years ago

We have a pretty good link processor in the web appbuilder widget: https://github.com/Esri/geoportal-server-catalog/blob/master/geoportal/src/main/webapp/viewer/widgets/GeoportalSearch/common/LinksProcessor.js

it looks at the URL and understands common patterns for Esri and OGC services. also includes some specific extensions for document-like links.

lindhor commented 3 years ago

Thanks for the hint. In my opinion this kind of string matching is the last resort, but maybe we will need to go that way. Currently it looks like the OGC handling only supports WMS and also would require the CSW records to include request=getcapabilities and service=wms in order to detect it correctly.

In many cases only the base service URL is returned which complicates detection. In our example the link looks like https://mapserver:443/MapService/WMS.axd/vmap0, i.e. without the parameters.

Of course it would be possible to add a check string matching for "WMS" anywhere in the URL or in the path part of the URL, but I would prefer to avoid it if we could find a more generic way. It would definitely not be sure that all CSW records for WMS services include the text "WMS" anywhere in the URL (and similarly for other kind of content.

We could have some kind of configurable mapping between URL substrings and types using regex to make it more flexible than today. That would however only solve the type detection. Looking at the current code we would still need to have code doing the specific service URL extraction from the value processed if I understand it correctly. I.e. stuff like:

      href = href.substr(0, href.indexOf("?"));

      href = href.substr(href.indexOf("url=")+4);
      href = decodeURIComponent(href);
      theLinkType = href.split("/").pop().toLowerCase();
mhogeweg commented 3 years ago

indeed. for ArcGIS based services, the analysis of the path is pretty good, as REST + OGC services provided via ArcGIS follow a defined pattern that cannot be changed. An alternative, but one that requires actually interacting with the link, is to test the link with for example a GetCapabilities request with a service parameter. if the response is valid for the service parameter then you know its type. Some metadata includes a link type element/attribute to go with the link. Although there is no enforced relationship between the type element/attribute and the link, it prevents having to interrogate the link itself.