navibyte / geospatial

Geospatial data structures, tools and utilities for Dart and Flutter.
Other
52 stars 5 forks source link

Check conformance classes known by OGC API Features #169

Closed navispatial closed 1 year ago

navispatial commented 1 year ago

The OGC API Features standard defines that a server should announce conformance classes supported by an API instance in /conformance resource.

A JSON response by a compliant server could be for example:

{
    "conformsTo": [
        "http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/core",
        "http://www.opengis.net/spec/ogcapi-common-2/1.0/conf/collections",
        "http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/core",
        "http://www.opengis.net/spec/ogcapi-features-1/1.0/req/oas30",
        "http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/html",
        "http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/geojson",
        "http://www.opengis.net/spec/ogcapi-features-4/1.0/conf/create-replace-delete"
    ]
}

See also official example by OGC.

Currently conformance classes could be checked by code like:

  // create an OGC API Features client
  final client = OGCAPIFeatures.http(endpoint: Uri.parse('https://example.org/api'));

  // conformance classes (text ids) informs the capabilities of the service
  final conformance = await client.conformance();

  // the service should be compliant with OGC API Features - Part 1 and GeoJSON
  const c1 = 'http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/core';
  const c2 = 'http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/geojson';
  if (conformance.contains(c1) && conformance.contains(c2)) {
    print('The service is compliant with OGC API Features, Part 1 - Core and GeoJSON');
  }

However a client code would have to know text identifiers, which is quite inconvenient.

The check should be something like this:

  if (conformance.conformsToFeaturesCore(geoJson: true)) {
    print('The service is compliant with OGC API Features, Part 1 - Core and GeoJSON');
  }
navispatial commented 1 year ago

Implemented by 2535da0

A new class OGCFeatureConformance was introduced:

import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';

/// A wrapper for conformance classes for a OGC API Features compliant service.
///
/// See [OGC API Features](https://github.com/opengeospatial/ogcapi-features).
///
/// This class can be used to check conformance classes for:
/// * `OGC API - Features - Part 1: Core`
/// * `OGC API - Features - Part 2: Coordinate Reference Systems by Reference`
@immutable
class OGCFeatureConformance extends Equatable {
  /// Conformance classes a service is conforming to.
  final Iterable<String> classes;

  /// Creates a wrapper for conformance classes a service is conforming to.
  const OGCFeatureConformance(this.classes);

  /// Check whether a service conforms to `OGC API - Features - Part 1: Core`
  ///
  /// Optionally also check whether it supports [openAPI30Class],
  /// [htmlClass], [geoJSONClass], [gmlSF0] and/or [gmlSF2].
  bool conformsToCore({
    bool? openAPI30,
    bool? html,
    bool? geoJSON,
    bool? gmlSF0,
    bool? gmlSF2,
  }) {
    var isCore = false;
    var isOpenAPI30 = false;
    var isHTML = false;
    var isGeoJSON = false;
    var isGMLSF0 = false;
    var isGMLSF2 = false;

    for (final id in classes) {
      if (!isCore && id == coreClass) {
        isCore = true;
      }
      if (!isOpenAPI30 && id == openAPI30Class) {
        isOpenAPI30 = true;
      }
      if (!isHTML && id == htmlClass) {
        isHTML = true;
      }
      if (!isGeoJSON && id == geoJSONClass) {
        isGeoJSON = true;
      }
      if (!isGMLSF0 && id == gmlSF0Class) {
        isGMLSF0 = true;
      }
      if (!isGMLSF2 && id == gmlSF2Class) {
        isGMLSF2 = true;
      }
    }

    return isCore &&
        (openAPI30 == null || isOpenAPI30 == openAPI30) &&
        (html == null || isHTML == html) &&
        (geoJSON == null || isGeoJSON == geoJSON) &&
        (gmlSF0 == null || isGMLSF0 == gmlSF0) &&
        (gmlSF2 == null || isGMLSF2 == gmlSF2);
  }

  /// Check whether a service conforms to
  /// `OGC API - Features - Part 2: Coordinate Reference Systems by Reference`.
  bool conformsToCrs() => classes.contains(crsClass);

  @override
  List<Object?> get props => [classes];

  static const coreClass =
      'http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/core';

  static const openAPI30Class =
      'http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/oas30';

  static const htmlClass =
      'http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/html';

  static const geoJSONClass =
      'http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/geojson';

  static const gmlSF0Class =
      'http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/gmlsf0';

  static const gmlSF2Class =
      'http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/gmlsf2';

  static const crsClass =
      'http://www.opengis.net/spec/ogcapi-features-2/1.0/conf/crs';
}