From cc246dd71631cfa082b214f15b760c8670e6dd62 Mon Sep 17 00:00:00 2001 From: eereiter Date: Tue, 3 Sep 2024 10:01:21 -0400 Subject: [PATCH] Cmr-10030: Ingesting and Searching for Visualizations (#2171) * CMR-10030: Adding in a version of the Visualization schema; ingest, indexing, and searching. * CMR-10030: adding in the real tiles schema. --- Generics.md | 6 +- common-lib/src/cmr/common/config.clj | 3 +- .../src/cmr/ingest/api/generic_documents.clj | 13 +- .../int_test/concepts/utils/generic.clj | 32 + schemas/resources/schemas/validate.py | 2 +- .../schemas/visualization/CHANGELOG.md | 35 + .../schemas/visualization/v1.0.0/index.json | 74 ++ .../schemas/visualization/v1.0.0/ingest.md | 85 ++ .../visualization/v1.0.0/metadata.json | 134 +++ .../schemas/visualization/v1.0.0/schema.json | 228 +++++ .../schemas/visualization/v1.0.0/search.md | 226 +++++ .../v1.0.0/spatial-temporal-extent.json | 912 ++++++++++++++++++ .../v1.0.0/umm-vis-maps-schema.json | 34 + .../v1.0.0/umm-vis-tiles-schema.json | 186 ++++ .../search/services/parameters/conversion.clj | 84 +- .../unit/data/elastic_search_index_test.clj | 3 +- .../unit/services/parameters_conversion.clj | 76 ++ .../system_int_test/utils/generic_util.clj | 5 + 18 files changed, 2073 insertions(+), 65 deletions(-) create mode 100644 schemas/resources/schemas/visualization/CHANGELOG.md create mode 100644 schemas/resources/schemas/visualization/v1.0.0/index.json create mode 100644 schemas/resources/schemas/visualization/v1.0.0/ingest.md create mode 100644 schemas/resources/schemas/visualization/v1.0.0/metadata.json create mode 100644 schemas/resources/schemas/visualization/v1.0.0/schema.json create mode 100644 schemas/resources/schemas/visualization/v1.0.0/search.md create mode 100644 schemas/resources/schemas/visualization/v1.0.0/spatial-temporal-extent.json create mode 100644 schemas/resources/schemas/visualization/v1.0.0/umm-vis-maps-schema.json create mode 100644 schemas/resources/schemas/visualization/v1.0.0/umm-vis-tiles-schema.json create mode 100644 search-app/test/cmr/search/test/unit/services/parameters_conversion.clj diff --git a/Generics.md b/Generics.md index 44405c62f0..04c994851c 100644 --- a/Generics.md +++ b/Generics.md @@ -12,7 +12,8 @@ If adding a new document, you will need to update the defconf variable by either :data-quality-summary ["1.0.0"] :order-option ["1.0.0"] :service-entry ["1.0.0"] - :service-option ["1.0.0"]} + :service-option ["1.0.0"] + :visualization ["1.0.0"]} :parser #(json/parse-string % true)}) When setting in an ENV or in AWS, use the JSON format: @@ -21,7 +22,8 @@ When setting in an ENV or in AWS, use the JSON format: \"data-quality-summary\": [\"1.0.0\"], \"order-option\": [\"1.0.0\"], \"service-entry\": [\"1.0.0\"], - \"service-option\": [\"1.0.0\"]}" + \"service-option\": [\"1.0.0\"] + \"visualization\": [\"1.0.0\"]}" Each setting consists of a key, which is the name for the Generic which must be unique, and a list of version numbers. These values *must* match parts of a file system path under "schemas". For example, the order-option value must resolve to: diff --git a/common-lib/src/cmr/common/config.clj b/common-lib/src/cmr/common/config.clj index d802ca4703..26732a218c 100644 --- a/common-lib/src/cmr/common/config.clj +++ b/common-lib/src/cmr/common/config.clj @@ -234,7 +234,8 @@ :variable-draft ["1.0.0"] :data-quality-summary-draft ["1.0.0"] :order-option-draft ["1.0.0"] - :grid-draft ["1.0.0"]} + :grid-draft ["1.0.0"] + :visualization ["1.0.0"]} :parser #(json/parse-string % true)}) (defconfig approved-pipeline-documentation diff --git a/ingest-app/src/cmr/ingest/api/generic_documents.clj b/ingest-app/src/cmr/ingest/api/generic_documents.clj index ea72a37ee0..ebc66ce1ba 100644 --- a/ingest-app/src/cmr/ingest/api/generic_documents.clj +++ b/ingest-app/src/cmr/ingest/api/generic_documents.clj @@ -52,12 +52,13 @@ (errors/throw-service-error :invalid-data (format "The %s schema is currently disabled and cannot be ingested." (util/html-escape schema))) - (if-some [schema-file (gconfig/read-schema-specification schema version)] - (let [schema-obj (js-validater/json-string->json-schema schema-file)] - (js-validater/validate-json schema-obj raw-json true)) - (errors/throw-service-error - :invalid-data - (format "While the [%s] schema with version [%s] is approved, it cannot be found." (util/html-escape schema) (util/html-escape version))))))) + (let [schema-path (format "schemas/%s/v%s/schema.json" (name schema) version) + schema-obj (js-validater/parse-json-schema-from-path schema-path)] + (if schema-obj + (js-validater/validate-json schema-obj raw-json true) + (errors/throw-service-error + :invalid-data + (format "While the [%s] schema with version [%s] is approved, it cannot be found." (util/html-escape schema) (util/html-escape version)))))))) (defn- concept-type->singular "Common task to convert concepts from their public URL form to their internal diff --git a/metadata-db-app/int-test/cmr/metadata_db/int_test/concepts/utils/generic.clj b/metadata-db-app/int-test/cmr/metadata_db/int_test/concepts/utils/generic.clj index 41f2bef2bb..b6fec26597 100644 --- a/metadata-db-app/int-test/cmr/metadata_db/int_test/concepts/utils/generic.clj +++ b/metadata-db-app/int-test/cmr/metadata_db/int_test/concepts/utils/generic.clj @@ -56,3 +56,35 @@ :extra-fields extra-fields} (dissoc attributes :extra-fields))] (concepts/create-any-concept provider-id concept-type uniq-num attributes))) + +(defmethod concepts/get-sample-metadata :visualization + [_concept-type] + (json/generate-string + {:Id "MODIS_Combined_L3_IGBP_Land_Cover_Type_Annual", + :VisualizationType "tiles", + :Name "Land Cover Type (L3, IGBP, Annual, Best Available, MODIS, Aqua+Terra)", + :What {:whattile1 "a", :whattile2 "b"}, + :How {:howtile1 "a", :howtile2 "b"}, + :ConceptIds [{:type "STD", + :value "C186286578-LPDAAC_ECS", + :shortName "MCD12Q1", + :title "MODIS/Terra+Aqua Land Cover Type Yearly L3 Global 500m SIN Grid V006", + :version "006", + :dataCenter "LPDAAC_ECS"}], + :MetadataSpecification {:URL "https://cdn.earthdata.nasa.gov/generics/visualization/v1.0.0", + :Name "Visualization", + :Version "1.0.0"}})) + +(defmethod concepts/create-concept :visualization + [concept-type & args] + (let [[provider-id uniq-num attributes] (concepts/parse-create-concept-args concept-type args) + native-id (str "vsl-native" uniq-num) + extra-fields (merge {:document-name "vsl-docname" + :schema "visualization"} + (:extra-fields attributes)) + attributes (merge {:user-id (str "user" uniq-num) + :format "application/json" + :native-id native-id + :extra-fields extra-fields} + (dissoc attributes :extra-fields))] + (concepts/create-any-concept provider-id concept-type uniq-num attributes))) diff --git a/schemas/resources/schemas/validate.py b/schemas/resources/schemas/validate.py index 4870fdab2e..0dce7f4939 100644 --- a/schemas/resources/schemas/validate.py +++ b/schemas/resources/schemas/validate.py @@ -54,7 +54,7 @@ def test_schema(schema_name): def main(): ret = test_schema("index") - for i in ["data-quality-summary", "grid", "order-option", "service-entry", "service-option"]: + for i in ["data-quality-summary", "grid", "order-option", "visualization"]: ret = ret + test_schema(i) ret = ret + test_index(i) diff --git a/schemas/resources/schemas/visualization/CHANGELOG.md b/schemas/resources/schemas/visualization/CHANGELOG.md new file mode 100644 index 0000000000..ecd6894393 --- /dev/null +++ b/schemas/resources/schemas/visualization/CHANGELOG.md @@ -0,0 +1,35 @@ +# visualization schema change Log + +## [1.0.0] +- 2024-??-?? +Initial release in the CMR. + +---- +Using version +https://git.earthdata.nasa.gov/projects/VISLABS/repos/metadata-mapping/browse/umm/visualization/v0.10.1 as of 08/29/2024 + +Changes to main.json: +- Changed the name of Identifier to Id +- Added Name to the schema. - Not sure if generics needs this - cant I just copy the title to Name? +- Changed ScienceKeywords array minItems from 0 to 1 +- Fixed the reference of SpatialExtentType to spatial-temporal-extent.json +- Moved the reference of TemporalExtentType to spatial-temporal-extent.json +- ConceptIds moved additionalProperties: false up to the type: object declaration +- MetadataSpecification is above the what and how. +- required + - Identifier changed name to id + - Name has been added - can this be Title? + - ConceptIds was added + - MetadataSpecification was added +- In the allOf section the What refernces are different - they don't look correct in the given schema +- In the allOf section the How refernces are different - they don't look correct in the given schema +- The rest is just different. + +Added the UMM-C 1.18.1 umm-cmn-json-schema.json Spatial, Temporal, and supporting elements to spatial-temporal-extent.json +Removed the older umm-cmn-json-schema.json. + + + +Copyright © 2024-2024 United States Government as represented by the +Administrator of the National Aeronautics and Space Administration. All Rights +Reserved. diff --git a/schemas/resources/schemas/visualization/v1.0.0/index.json b/schemas/resources/schemas/visualization/v1.0.0/index.json new file mode 100644 index 0000000000..90a232c62f --- /dev/null +++ b/schemas/resources/schemas/visualization/v1.0.0/index.json @@ -0,0 +1,74 @@ +{ + "MetadataSpecification": { + "URL": "https://cdn.earthdata.nasa.gov/generic/index/v0.0.1", + "Name": "Generic-Index", + "Version": "0.0.1" + }, + "Generic": { + "Name": "Visualization", + "Version": "1.0.0" + }, + "SubConceptType": "VIS", + "IndexSetup" : { + "index": { + "number_of_shards": 3, + "number_of_replicas": 1, + "refresh_interval": "1s" + } + }, + "Indexes": + [ + { + "Description": "Identifier", + "Field": ".Identifier", + "Name": "Id", + "Mapping": "string" + }, + { + "Description": "Identifier", + "Field": ".Identifier", + "Name": "Identifier", + "Mapping": "string" + }, + { + "Description": "Schema Name as the Name field", + "Field": ".Name", + "Name": "Name", + "Mapping": "string" + }, + { + "Description": "Schema Title as the Title field", + "Field": ".Title", + "Name": "Title", + "Mapping": "string" + }, + { + "Description": "VisualizationType", + "Field": ".VisualizationType", + "Name": "Visualization-Type", + "Mapping": "string" + }, + { + "Description": "Visualization Source ConceptIds", + "Field": ".ConceptIds", + "Name": "Concept-Ids", + "Mapping": "string", + "Indexer": "simple-array-field", + "Configuration": {"sub-fields": ["value", "shortName"]} + }, + { + "Description": "Visualization Source ConceptIds in keywords", + "Field": ".ConceptIds", + "Name": "keyword", + "Mapping": "string", + "Indexer": "simple-array-field", + "Configuration": {"sub-fields": ["value"]} + }, + { + "Description": "Identifier with the keywords", + "Field": ".Identifier", + "Name": "keyword", + "Mapping": "string" + } + ] +} diff --git a/schemas/resources/schemas/visualization/v1.0.0/ingest.md b/schemas/resources/schemas/visualization/v1.0.0/ingest.md new file mode 100644 index 0000000000..3583f8df38 --- /dev/null +++ b/schemas/resources/schemas/visualization/v1.0.0/ingest.md @@ -0,0 +1,85 @@ +## Visualization + +#### /providers/<provider-id>/visualizations/<native-id> + +### Create / Update a Visualization + +Visualization concepts can be created or updated by sending an HTTP PUT with the metadata to the URL `%CMR-ENDPOINT%/providers//visualizations/`. The response will include the [concept id](#concept-id) and the [revision id](#revision-id). The contents of the metadata is passed in the body of the request. + +``` + curl -XPOST \ + -H "Cmr-Pretty: true" \ + -H "Content-Type:application/vnd.nasa.cmr.umm+json" \ + -H "Authorization: Bearer XXXX" \ + "%CMR-ENDPOINT%/providers/PROV1/visualizations/sampleNativeId" \ + -d @sampleVisualization.json +``` + +#### Successful Response in XML + +``` + + + VIS1200000000-PROV1 + 1 + + + +``` +Subsequent ingests to the Visualization record will result in updates to it's metadata as well as increment the revision-id of the record. + +#### Successful Response in JSON + +By passing the option `-H "Accept: application/json"` to `curl`, one may +get a JSON response: + +``` + {"concept-id":"VIS1200000000-PROV1","revision-id":1,"warnings":null,"existing-errors":null} +``` + +### Delete a Visualization + +Visualization metadata can be deleted by sending an HTTP DELETE to the URL `%CMR-ENDPOINT%/providers//visualizations/`. The response will include the [concept id](#concept-id) and the [revision id](#revision-id) of the tombstone. + +``` + curl -XDELETE \ + -H "Cmr-Pretty: true" \ + -H "Authorization: Bearer XXXX" \ + %CMR-ENDPOINT%/providers/PROV1/visualizations/sampleNative23Id" +``` + +#### Successful Response in XML + +``` + + + VIS1200000000-PROV1 + 2 + +``` + +#### Successful Response in JSON + +``` + {"concept-id":"VIS1200000000-PROV1","revision-id":2,"warnings":null,"existing-errors":null} +``` + +Attempting to delete an already deleted record will return +the following error message + +#### Unsuccessful Response in XML + +``` + + + Concept with native-id [sampleNative23Id] and concept-id [VIS1200000000-PROV1] is already deleted. + +``` + +#### Unsuccessful Response in JSON + +``` + "errors": [ + "Concept with native-id [sampleNative23Id] and concept-id [VIS1200000000-PROV1] is already deleted." + ] +``` diff --git a/schemas/resources/schemas/visualization/v1.0.0/metadata.json b/schemas/resources/schemas/visualization/v1.0.0/metadata.json new file mode 100644 index 0000000000..a3d48b9c01 --- /dev/null +++ b/schemas/resources/schemas/visualization/v1.0.0/metadata.json @@ -0,0 +1,134 @@ +{ + "Identifier": "MODIS_Combined_L3_IGBP_Land_Cover_Type_Annual", + "Name": "MODIS_Combined_L3_IGBP_Land_Cover_Type_Annual", + + "VisualizationType": "tiles", + + "Title": "Land Cover Type (L3, IGBP, Annual, Best Available, MODIS, Aqua+Terra)", + + "Subtitle": "Aqua+Terra MODIS", + "Description": "The Terra and Aqua combined Moderate Resolution Imaging Spectroradiometer (MODIS) Land Cover Type (MCD12Q1) Version 6 data product provides global land cover types at yearly intervals. The MCD12Q1 Version 6 data product is derived using supervised classifications of MODIS Terra and Aqua reflectance data. The supervised classifications then undergo additional post-processing that incorporate prior knowledge and ancillary information to further refine specific classes. This layer defines land cover type based on the International Geosphere-Biosphere Programme (IGBP) classification scheme. The classification legend and class descriptions are listed in section 5 of the User Guide", + + "ScienceKeywords": [ + { + "Category": "EARTH SCIENCE", + "Topic": "SPECTRAL/ENGINEERING", + "Term": "tbd", + "VariableLevel1": "tbd", + "DetailedVariable": "tbd" + }, + { + "Category": "EARTH SCIENCE SERVICES", + "Topic": "DATA ANALYSIS AND VISUALIZATION", + "Term": "DATA VISUALIZATION", + "VariableLevel1": "tbd" + } + ], + + "TemporalExtents": [ + { + "RangeDateTimes": [ + { + "BeginningDateTime": "2001-01-01T00:00:00.000Z", + "EndingDateTime": "2019-12-31T23:59:59.999Z" + } + ] + } + ], + + "What": { + + "SupportsContinuousBrowsing": false, + + "Dimension": { + "Current": "false", + "Default": "2018-01-01", + "Value": "2001-01-01/2018-01-01/P1Y", + "ows:Identifier": "Time", + "ows:UOM": "ISO8601" + }, + "Format": "image/png", + "ResourceURL": { + "format": "image/png", + "resourceType": "tile", + "template": "https://gibs.earthdata.nasa.gov/wmts/epsg4326/best/MODIS_Combined_L3_IGBP_Land_Cover_Type_Annual/default/{Time}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png" + }, + "LegendURL": [ + { + "format": "image/svg+xml", + "height": "288", + "width": "288", + "xlink:href": "https://gibs.earthdata.nasa.gov/legends/MODIS_IGBP_Land_Cover_Type_V.svg", + "@xlink:role": "http://earthdata.nasa.gov/gibs/legend-type/vertical", + "@xlink:title": "GIBS Color Map Legend: Vertical", + "@xlink:type": "simple" + }, + { "@format": "image/svg+xml", + "@height": "270", + "@width": "270", + "@xlink:href": "https://gibs.earthdata.nasa.gov/legends/MODIS_IGBP_Land_Cover_Type_H.svg", + "@xlink:role": "http://earthdata.nasa.gov/gibs/legend-type/horizontal", + "@xlink:title": "GIBS Color Map Legend: Horizontal", + "@xlink:type": "simple" + } + ], + "Metadata": [ + { + "@xlink:href": "https://gibs.earthdata.nasa.gov/colormaps/v1.3/MODIS_IGBP_Land_Cover_Type.xml", + "@xlink:role": "http://earthdata.nasa.gov/gibs/metadata-type/colormap", + "@xlink:title": "GIBS Color Map: Data - RGB Mapping", + "@xlink:type": "simple" + }, + { + "@xlink:href": "https://gibs.earthdata.nasa.gov/colormaps/v1.0/MODIS_IGBP_Land_Cover_Type.xml", + "@xlink:role": "http://earthdata.nasa.gov/gibs/metadata-type/colormap/1.0", + "@xlink:title": "GIBS Color Map: Data - RGB Mapping", + "@xlink:type": "simple" + }, + { + "@xlink:href": "https://gibs.earthdata.nasa.gov/colormaps/v1.3/MODIS_IGBP_Land_Cover_Type.xml", + "@xlink:role": "http://earthdata.nasa.gov/gibs/metadata-type/colormap/1.3", + "@xlink:title": "GIBS Color Map: Data - RGB Mapping", + "@xlink:type": "simple" + } + ], + "TileMatrixSetLink": + { + "TileMatrixSet": "500m" + }, + "WGS84BoundingBox": { + "crs": "urn:ogc:def:crs:OGC:2:84", + "LowerCorner": [-180, -90], + "UpperCorner": [180, 90] + }, + + "Ongoing": false, + "measurement": "Land Cover", + "RetentionPeriod": -1, + "daynight": [ + "day" + ], + "layerPeriod": "Yearly" + + }, + + "How": { + }, + + "ConceptIds": [ + { + "type": "STD", + "value": "C186286578-LPDAAC_ECS", + "shortName": "MCD12Q1", + "title": "MODIS/Terra+Aqua Land Cover Type Yearly L3 Global 500m SIN Grid V006", + "version": "006", + "dataCenter": "LPDAAC_ECS" + } + ], + + "MetadataSpecification": { + "URL": "https://cdn.earthdata.nasa.gov/generics/visualization/v1.0.0", + "Name": "Visualization", + "Version": "1.0.0" + } +} \ No newline at end of file diff --git a/schemas/resources/schemas/visualization/v1.0.0/schema.json b/schemas/resources/schemas/visualization/v1.0.0/schema.json new file mode 100644 index 0000000000..ada763c77e --- /dev/null +++ b/schemas/resources/schemas/visualization/v1.0.0/schema.json @@ -0,0 +1,228 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "UMM-Vis", + "type": "object", + "additionalProperties": false, + + "properties": { + "Identifier": { + "description": "The unique identifier for the visualization.", + "type": "string", + "minLength": 1, + "maxLength": 256 + }, + "Name": { + "description": "A name of the visualization.", + "type": "string", + "minLength": 1, + "maxLength": 256 + }, + "Title": { + "description": "A short descriptive title of the visualization.", + "type": "string", + "minLength": 1, + "maxLength": 256 + }, + "Subtitle": { + "description": "A short descriptive subtitle of the visualization.", + "type": "string", + "minLength": 1, + "maxLength": 256 + }, + "Description": { + "description": "A human readable description of the visualization written using HTML notation for advanced text. The goal is to create descriptions for the science-minded public that may have an interest in finding out what the visualization shows, why it’s important, how it was created, etc...", + "type": "string", + "minLength": 1, + "maxLength": 1024 + }, + + "ScienceKeywords": { + "description": "Earth Science keywords that are representative of the data being visualized. The controlled vocabulary for Science Keywords is maintained in the Keyword Management System (KMS).", + "type": "array", + "items": { + "$ref": "#/definitions/ScienceKeywordType" + }, + "minItems": 1 + }, + + "SpatialExtent": { + "$ref": "spatial-temporal-extent.json#/definitions/SpatialExtentType" + }, + + "TemporalExtents": { + "description": "This class contains attributes which describe the temporal range of a specific layer. Temporal Extent includes a specification of the Temporal Range Type of the collection, which is one of Range Date Time, Single Date Time, or Periodic Date Time", + "type": "array", + "items": { + "$ref": "spatial-temporal-extent.json#/definitions/TemporalExtentType" + }, + "minItems": 1 + }, + + "ConceptIds": { + "description": "Which CMR dataset(s) are represented by the visualization.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "Identify whether the associated dataset is NRT or STD.", + "type": "string", + "enum": [ + "NRT", + "STD" + ] + }, + "value": { + "description": "The dataset's CMR concept id", + "type": "string" + }, + "dataCenter": { + "type": "string" + }, + "shortName": { + "type": "string" + }, + "title": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "required": ["type", "value"] + }, + "minItems": 1 + }, + + "VisualizationType": { + "type": "string", + "enum": ["tiles", "maps"] + }, + + "MetadataSpecification": { + "description": "Required to add in schema information into every record. It includes the schema's name, version, and URL location. The information is controlled through enumerations at the end of this schema.", + "$ref": "#/definitions/MetadataSpecificationType" + }, + + "What": { + "$comment": "must be here when additionalProperties==false. otherwise fails" + }, + + "How": { + "$comment": "must be here when additionalProperties==false. otherwise fails" + } + }, + "required": [ + "Identifier", + "Name", + "VisualizationType", + "What", + "How", + "ConceptIds", + "MetadataSpecification" + ], + + "allOf": [ + { + "if": { + "properties": { + "VisualizationType": {"const": "tiles"} + } + }, + "then": { + "properties": { + "What": { + "$ref": "umm-vis-tiles-schema.json#/definitions/What" + }, + "How": { + "$ref": "umm-vis-tiles-schema.json#/definitions/How" + } + } + } + }, + + { + "if": { + "properties": { + "VisualizationType": {"const": "maps"} + } + }, + "then": { + "properties": { + "What": { + "$ref": "umm-vis-maps-schema.json#/definitions/What" + }, + "How": { + "$ref": "umm-vis-maps-schema.json#/definitions/How" + } + } + } + } + ], + + "definitions": { + + "ScienceKeywordType": { + "type": "object", + "additionalProperties": false, + "description": "Enables specification of Earth science keywords related to the collection. The Earth Science keywords are chosen from a controlled keyword hierarchy maintained in the Keyword Management System (KMS). The valid values can be found at the KMS website: https://gcmdservices.gsfc.nasa.gov/kms/concepts/concept_scheme/sciencekeywords?format=csv.", + "properties": { + "Category": { + "$ref": "#/definitions/KeywordStringType" + }, + "Topic": { + "$ref": "#/definitions/KeywordStringType" + }, + "Term": { + "$ref": "#/definitions/KeywordStringType" + }, + "VariableLevel1": { + "$ref": "#/definitions/KeywordStringType" + }, + "VariableLevel2": { + "$ref": "#/definitions/KeywordStringType" + }, + "VariableLevel3": { + "$ref": "#/definitions/KeywordStringType" + }, + "DetailedVariable": { + "$ref": "#/definitions/KeywordStringType" + } + }, + "required": ["Category", "Topic", "Term"] + }, + "KeywordStringType": { + "description": "The keyword string type for science keywords. The pattern below helps to minimize the types of characters allowed in the keywords to help users minimize keyword issues.", + "type": "string", + "minLength": 1, + "maxLength": 80, + "pattern": "[\\w\\-&'()\\[\\]/.\"#$%\\^@!*+=,][\\w\\-&'()\\[\\]/.\"#$%\\^@!*+=, ]{1,79}" + }, + + "MetadataSpecificationType": { + "type": "object", + "additionalProperties": false, + "description": "This object requires any metadata record that is validated by this schema to provide information about the schema.", + "properties": { + "URL": { + "description": "This element represents the URL where the schema lives. The schema can be downloaded.", + "type": "string", + "enum": ["https://cdn.earthdata.nasa.gov/generics/visualization/v1.0.0"] + }, + "Name": { + "description": "This element represents the name of the schema.", + "type": "string", + "enum": ["Visualization"] + }, + "Version": { + "description": "This element represents the version of the schema.", + "type": "string", + "enum": ["1.0.0"] + } + }, + "required": ["URL", "Name", "Version"] + } + } +} + \ No newline at end of file diff --git a/schemas/resources/schemas/visualization/v1.0.0/search.md b/schemas/resources/schemas/visualization/v1.0.0/search.md new file mode 100644 index 0000000000..aa9078b89b --- /dev/null +++ b/schemas/resources/schemas/visualization/v1.0.0/search.md @@ -0,0 +1,226 @@ +### Visualization + +Visualizations inform users about different kinds of visualizations, whether they come from GIBS, GIOVANNI, WorldView, or other sources. Visualization metadata is stored in the JSON format [Visualization Schema](https://git.earthdata.nasa.gov/projects/EMFD/repos/otherschemas/browse/visualization). + +#### Searching for Visualizations + +Visualizations can be searched for by sending a request to `%CMR-ENDPOINT%/visualizations`. XML reference, JSON and UMM JSON response formats are supported. + +Visualization search results are paged. See [Paging Details](#paging-details) for more information on how to page through search results. + +##### Visualization Search Parameters + +The following parameters are supported when searching for Visualizations. + +##### Standard Parameters + +* page\_size +* page\_num +* pretty + +##### Visualization Matching Parameters + +These parameters will match fields within a Visualization. They are case insensitive by default. They support options specified. They also support searching with multiple values in the style of `name[]=key1&name[]=key2`. The values are bitwise `OR`ed together. + +* name - options: pattern, ignore\_case +* provider - options: pattern, ignore\_case +* native\_id - options: pattern, ignore\_case +* concept\_id +* id or identifier +* title +* visualization-type +* concept-ids + +``` + curl "%CMR-ENDPOINT%/visualizations?concept_id=VIS1200000000-PROV1" +``` + +##### Visualization Search Response + +##### XML Reference + +The XML reference response format is used for returning references to search results. It consists of the following fields: + +| Field | Description | +| ---------- | -------------------------------------------------- | +| hits | the number of results matching the search query | +| took | time in milliseconds it took to perform the search | +| references | identifying information about each search result | + +The `references` field may contain multiple `reference` entries, each consisting of the following fields: + +| Field | Description | +| ----------- | ------------------------------------------------------------------ | +| name | the value of the Name field in Visualization metadata. | +| id | the CMR identifier for the result | +| location | the URL at which the full metadata for the result can be retrieved | +| revision-id | the internal CMR version number for the result | + +__Example__ + +``` + curl -H "Cmr-Pretty: true" \ + "%CMR-ENDPOINT%/visualizations.xml?name=visualization-name-v1" +``` + +__Sample response__ + +``` + + + 1 + 13 + + + visualization-name-v1 + VIS1200000000-PROV1 + %CMR-ENDPOINT%/concepts/VIS1200000000-PROV1/4 + 4 + + + +``` + +##### JSON + +The JSON response includes the following fields. + +* hits - How many total Visualizations were found. +* took - How long the search took in milliseconds +* items - a list of the current page of Visualizations with the following fields + * concept\_id + * revision\_id + * provider\_id + * native\_id + * name + * id + +__Example__ + +``` + curl -H "Cmr-Pretty: true" \ + "%CMR-ENDPOINT%/visualizations.json?name="visualization-name-v1" +``` + +__Sample response__ + +``` + { + "hits": 1, + "took": 10, + "items": [ + { + "concept_id": "VIS1200000000-PROV1", + "revision\_id": 4, + "provider\_id": "PROV-1", + "native\_id": "sampleNative-Id", + "name": "visualization-name-v1", + "id": "visualization-id" + } + ] + } +``` + +##### UMM JSON + +The UMM JSON response contains meta-metadata of the Visualization, the UMM fields and the associations field if applicable. [To search over specific versions of UMM](#umm-json). + +__Example__ + +``` + curl -H "pretty=true" \ + "%CMR-ENDPOINT%/visualizations.umm_json?name=visualization-name-v1" +``` + +__Sample response__ + +``` + { + "hits": 1, + "took": 17, + "items": [ + { + "meta": { + "revision-id": 1, + "deleted": false, + "provider-id": "PROV1", + "user-id": "exampleuser", + "native-id": "samplenativeid12", + "concept-id": "VIS1200000000-PROV1", + "revision-date": "2022-10-26T19:17:27.021Z", + "concept-type": "visualization" + }, + "umm": { + "Id": "visualization-id", + "Name": "visualization-name-v1", + "Title": "Title_1", + "MetadataSpecification": { + "Name": "Visualization", + "Version": "1.0.0", + "URL": "https://cdn.earthdata.nasa.gov/generics/visualization/1.0.0" + } + } + } + ] + } +``` + +#### Retrieving All Revisions of a Visualization + +In addition to retrieving the latest revision for a Visualization parameter search, it is possible to return all revisions, including tombstone (deletion marker) revisions, by passing in `all_revisions=true` with the URL parameters. The reference, JSON, and UMM JSON response formats are supported for all revision searches merely change to 'umm_json' and 'json' respectively. References to tombstone revisions do not include the `location` tag and include an additional tag, `deleted`, which always has content of "true". Visualizations with only one revision will of course, return only one result. + +__Example__ + +``` + curl -H "Cmr-Pretty: true" \ + "%CMR-ENDPOINT%/visualizations.xml?concept_id=VIS1200000000-PROV1&all_revisions=true" +``` + +__Sample response__ + +``` + + + 4 + 80 + + + visualization-name-v1 + VIS1200000000-PROV1 + true + 1 + + + visualization-name-v2 + VIS1200000000-PROV1V + %CMR-ENDPOINT%/concepts/VIS1200000000-PROV1/3 + 2 + + + visualization-name-v3 + VIS1200000000-PROV1 + %CMR-ENDPOINT%/concepts/VIS1200000000-PROV1/4 + 3 + + + +``` + +#### Sorting Visualization Results + +By default, Visualization results are sorted by name, then by provider-id. + +One or more sort keys can be specified using the sort_key[] parameter. The order used impacts searching. Fields can be prepended with a - to sort in descending order. Ascending order is the default but + (Note: + must be URL encoded as %2B) can be used to explicitly request ascending. + +##### Valid Visualization Sort Keys + +* name +* provider +* revision_date + +Examples of sorting by name in descending (reverse alphabetical) and ascending orders (Note: the `+` must be escaped with %2B): + +``` + curl "%CMR-ENDPOINT%/visualizations?sort_key\[\]=-name" + curl "%CMR-ENDPOINT%/visualizations?sort_key\[\]=%2Bname" +``` diff --git a/schemas/resources/schemas/visualization/v1.0.0/spatial-temporal-extent.json b/schemas/resources/schemas/visualization/v1.0.0/spatial-temporal-extent.json new file mode 100644 index 0000000000..dd4bf370a7 --- /dev/null +++ b/schemas/resources/schemas/visualization/v1.0.0/spatial-temporal-extent.json @@ -0,0 +1,912 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "SpatialExtentType": { + "type": "object", + "additionalProperties": false, + "description": "Specifies the geographic and vertical (altitude, depth) coverage of the data.", + "properties": { + "SpatialCoverageType": { + "description": "Denotes whether the collection's spatial coverage requires horizontal, vertical, horizontal and vertical, orbit, or vertical and orbit in the spatial domain and coordinate system definitions.", + "$ref": "#/definitions/SpatialCoverageTypeEnum" + }, + "HorizontalSpatialDomain": { + "$ref": "#/definitions/HorizontalSpatialDomainType" + }, + "VerticalSpatialDomains": { + "type": "array", + "items": { + "$ref": "#/definitions/VerticalSpatialDomainType" + } + }, + "OrbitParameters": { + "$ref": "#/definitions/OrbitParametersType" + }, + "GranuleSpatialRepresentation": { + "$ref": "#/definitions/GranuleSpatialRepresentationEnum" + } + }, + "required": ["GranuleSpatialRepresentation"], + "allOf": [{"$ref": "#/definitions/OrbitParameterExistsIfGranuleSpatialRepresentationIsOrbit"}] + }, + "SpatialCoverageTypeEnum": { + "type": "string", + "enum": ["EARTH/GLOBAL", "HORIZONTAL", "VERTICAL", "ORBITAL", "HORIZONTAL_VERTICAL", "ORBITAL_VERTICAL", "HORIZONTAL_ORBITAL", "HORIZONTAL_VERTICAL_ORBITAL", "LUNAR"] + }, + "HorizontalSpatialDomainType": { + "type": "object", + "additionalProperties": false, + "description": "Information about a collection with horizontal spatial coverage.", + "properties": { + "ZoneIdentifier": { + "description": "The appropriate numeric or alpha code used to identify the various zones in the collection's grid coordinate system.", + "type": "string", + "minLength": 1, + "maxLength": 80 + }, + "Geometry": { + "$ref": "#/definitions/GeometryType" + }, + "ResolutionAndCoordinateSystem": { + "description": "Specifies the horizontal spatial extents coordinate system and its resolution.", + "$ref": "#/definitions/ResolutionAndCoordinateSystemType" + } + }, + "required": ["Geometry"] + }, + "GeometryType": { + "type": "object", + "additionalProperties": false, + "properties": { + "CoordinateSystem": { + "$ref": "#/definitions/CoordinateSystemEnum" + }, + "Points": { + "type": "array", + "items": { + "$ref": "#/definitions/PointType" + }, + "minItems": 1 + }, + "BoundingRectangles": { + "type": "array", + "items": { + "$ref": "#/definitions/BoundingRectangleType" + }, + "minItems": 1 + }, + "GPolygons": { + "type": "array", + "items": { + "$ref": "#/definitions/GPolygonType" + }, + "minItems": 1 + }, + "Lines": { + "type": "array", + "items": { + "$ref": "#/definitions/LineType" + }, + "minItems": 1 + } + }, + "required": ["CoordinateSystem"], + "anyOf": [{ + "required": ["Points"] + }, { + "required": ["BoundingRectangles"] + }, { + "required": ["GPolygons"] + }, { + "required": ["Lines"] + }] + }, + "CoordinateSystemEnum": { + "type": "string", + "enum": ["CARTESIAN", "GEODETIC"] + }, + "PointType": { + "type": "object", + "additionalProperties": false, + "description": "The longitude and latitude values of a spatially referenced point in degrees.", + "properties": { + "Longitude": { + "$ref": "#/definitions/LongitudeType" + }, + "Latitude": { + "$ref": "#/definitions/LatitudeType" + } + }, + "required": ["Longitude", "Latitude"] + }, + "LatitudeType": { + "description": "The latitude value of a spatially referenced point, in degrees. Latitude values range from -90 to 90.", + "type": "number", + "minimum": -90, + "maximum": 90 + }, + "LongitudeType": { + "description": "The longitude value of a spatially referenced point, in degrees. Longitude values range from -180 to 180.", + "type": "number", + "minimum": -180, + "maximum": 180 + }, + "BoundingRectangleType": { + "type": "object", + "additionalProperties": false, + "properties": { + "WestBoundingCoordinate": { + "$ref": "#/definitions/LongitudeType" + }, + "NorthBoundingCoordinate": { + "$ref": "#/definitions/LatitudeType" + }, + "EastBoundingCoordinate": { + "$ref": "#/definitions/LongitudeType" + }, + "SouthBoundingCoordinate": { + "$ref": "#/definitions/LatitudeType" + } + }, + "required": ["WestBoundingCoordinate", "NorthBoundingCoordinate", "EastBoundingCoordinate", "SouthBoundingCoordinate"] + }, + "GPolygonType": { + "type": "object", + "additionalProperties": false, + "properties": { + "Boundary": { + "$ref": "#/definitions/BoundaryType" + }, + "ExclusiveZone": { + "$ref": "#/definitions/ExclusiveZoneType" + } + }, + "required": ["Boundary"] + }, + "BoundaryType": { + "type": "object", + "additionalProperties": false, + "description": "A boundary is set of points connected by straight lines representing a polygon on the earth. It takes a minimum of three points to make a boundary. Points must be specified in counter-clockwise order and closed (the first and last vertices are the same).", + "properties": { + "Points": { + "type": "array", + "items": { + "$ref": "#/definitions/PointType" + }, + "minItems": 4 + } + }, + "required": ["Points"] + }, + "ExclusiveZoneType": { + "type": "object", + "additionalProperties": false, + "description": "Contains the excluded boundaries from the GPolygon.", + "properties": { + "Boundaries": { + "type": "array", + "items": { + "$ref": "#/definitions/BoundaryType" + }, + "minItems": 1 + } + }, + "required": ["Boundaries"] + }, + "LineType": { + "type": "object", + "additionalProperties": false, + "properties": { + "Points": { + "type": "array", + "items": { + "$ref": "#/definitions/PointType" + }, + "minItems": 2 + } + }, + "required": ["Points"] + }, + "VerticalSpatialDomainType": { + "type": "object", + "additionalProperties": false, + "properties": { + "Type": { + "description": "Describes the type of the area of vertical space covered by the collection locality.", + "$ref": "#/definitions/VerticalSpatialDomainTypeEnum" + }, + "Value": { + "description": "Describes the extent of the area of vertical space covered by the collection. Must be accompanied by an Altitude Encoding Method description. The datatype for this attribute is the value of the attribute VerticalSpatialDomainType. The unit for this attribute is the value of either DepthDistanceUnits or AltitudeDistanceUnits.", + "type": "string", + "minLength": 1, + "maxLength": 80 + } + }, + "required": ["Type", "Value"] + }, + "VerticalSpatialDomainTypeEnum": { + "type": "string", + "enum": ["Atmosphere Layer", "Maximum Altitude", "Maximum Depth", "Minimum Altitude", "Minimum Depth"] + }, + "FootprintType": { + "type": "object", + "additionalProperties": false, + "description": "The largest width of an instrument's footprint as measured on the Earths surface. The largest Footprint takes the place of SwathWidth in the Orbit Backtrack Algorithm if SwathWidth does not exist. The optional description element allows the user of the record to be able to distinguish between the different footprints of an instrument if it has more than 1.", + "properties": { + "Footprint": { + "description": "The largest width of an instrument's footprint as measured on the Earths surface. The largest Footprint takes the place of SwathWidth in the Orbit Backtrack Algorithm if SwathWidth does not exist.", + "type": "number" + }, + "FootprintUnit": { + "description": "The Footprint value's unit.", + "type": "string", + "enum": ["Kilometer", "Meter"] + }, + "Description": { + "description": "The description element allows the user of the record to be able to distinguish between the different footprints of an instrument if it has more than 1.", + "type": "string" + } + }, + "required": ["Footprint", "FootprintUnit"] + }, + "OrbitParametersType": { + "description": "Orbit parameters for the collection used by the Orbital Backtrack Algorithm.", + "oneOf":[{ + "type": "object", + "title": "Orbit parameters with just swath", + "additionalProperties": false, + "properties": { + "SwathWidth": { + "description": "Total observable width of the satellite sensor nominally measured at the equator.", + "type": "number" + }, + "SwathWidthUnit": { + "description": "The SwathWidth value's unit.", + "type": "string", + "enum": ["Kilometer", "Meter"] + }, + "OrbitPeriod": { + "description": "The time in decimal minutes the satellite takes to make one full orbit.", + "type": "number" + }, + "OrbitPeriodUnit": { + "description": "The Orbit Period value's unit.", + "type": "string", + "enum": ["Decimal Minute"] + }, + "InclinationAngle": { + "description": "The heading of the satellite as it crosses the equator on the ascending pass. This is the same as (180-declination) and also the same as the highest latitude achieved by the satellite.", + "type": "number" + }, + "InclinationAngleUnit": { + "description": "The InclinationAngle value's unit.", + "type": "string", + "enum": ["Degree"] + }, + "NumberOfOrbits": { + "description": "The number of full orbits composing each granule. This may be a fraction of an orbit.", + "type": "number" + }, + "StartCircularLatitude": { + "description": "The latitude start of the orbit relative to the equator. This is used by the backtrack search algorithm to treat the orbit as if it starts from the specified latitude. This is optional and will default to 0 if not specified.", + "type": "number" + }, + "StartCircularLatitudeUnit": { + "description": "The StartCircularLatitude value's unit.", + "type": "string", + "enum": ["Degree"] + } + }, + "required": ["SwathWidth", "SwathWidthUnit", "OrbitPeriod", "OrbitPeriodUnit", "InclinationAngle", "InclinationAngleUnit", "NumberOfOrbits"], + "dependencies": { + "StartCircularLatitude": ["StartCircularLatitudeUnit"] + } + }, { + "type": "object", + "title": "Orbit parameters with just footprints", + "additionalProperties": false, + "properties": { + "Footprints" : { + "description": "A list of instrument footprints or field of views. A footprint holds the largest width of the described footprint as measured on the earths surface along with the width's unit. An optional description element exists to be able to distinguish between the footprints, if that is desired. This element is optional. If this element is used at least 1 footprint must exist in the list.", + "type": "array", + "items": { + "$ref": "#/definitions/FootprintType" + }, + "minItems": 1 + }, + "OrbitPeriod": { + "description": "The time in decimal minutes the satellite takes to make one full orbit.", + "type": "number" + }, + "OrbitPeriodUnit": { + "description": "The Orbit Period value's unit.", + "type": "string", + "enum": ["Decimal Minute"] + }, + "InclinationAngle": { + "description": "The heading of the satellite as it crosses the equator on the ascending pass. This is the same as (180-declination) and also the same as the highest latitude achieved by the satellite.", + "type": "number" + }, + "InclinationAngleUnit": { + "description": "The InclinationAngle value's unit.", + "type": "string", + "enum": ["Degree"] + }, + "NumberOfOrbits": { + "description": "The number of full orbits composing each granule. This may be a fraction of an orbit.", + "type": "number" + }, + "StartCircularLatitude": { + "description": "The latitude start of the orbit relative to the equator. This is used by the backtrack search algorithm to treat the orbit as if it starts from the specified latitude. This is optional and will default to 0 if not specified.", + "type": "number" + }, + "StartCircularLatitudeUnit": { + "description": "The StartCircularLatitude value's unit.", + "type": "string", + "enum": ["Degree"] + } + }, + "required": ["Footprints", "OrbitPeriod", "OrbitPeriodUnit", "InclinationAngle", "InclinationAngleUnit", "NumberOfOrbits"], + "dependencies": { + "StartCircularLatitude": ["StartCircularLatitudeUnit"] + } + }, { + "type": "object", + "title": "Orbit parameters with both swathwidth and footprints", + "additionalProperties": false, + "properties": { + "SwathWidth": { + "description": "Total observable width of the satellite sensor nominally measured at the equator.", + "type": "number" + }, + "SwathWidthUnit": { + "description": "The SwathWidth value's unit.", + "type": "string", + "enum": ["Kilometer", "Meter"] + }, + "Footprints" : { + "description": "A list of instrument footprints or field of views. A footprint holds the largest width of the described footprint as measured on the earths surface along with the width's unit. An optional description element exists to be able to distinguish between the footprints, if that is desired. This element is optional. If this element is used at least 1 footprint must exist in the list.", + "type": "array", + "items": { + "$ref": "#/definitions/FootprintType" + }, + "minItems": 1 + }, + "OrbitPeriod": { + "description": "The time in decimal minutes the satellite takes to make one full orbit.", + "type": "number" + }, + "OrbitPeriodUnit": { + "description": "The Orbit Period value's unit.", + "type": "string", + "enum": ["Decimal Minute"] + }, + "InclinationAngle": { + "description": "The heading of the satellite as it crosses the equator on the ascending pass. This is the same as (180-declination) and also the same as the highest latitude achieved by the satellite.", + "type": "number" + }, + "InclinationAngleUnit": { + "description": "The InclinationAngle value's unit.", + "type": "string", + "enum": ["Degree"] + }, + "NumberOfOrbits": { + "description": "The number of full orbits composing each granule. This may be a fraction of an orbit.", + "type": "number" + }, + "StartCircularLatitude": { + "description": "The latitude start of the orbit relative to the equator. This is used by the backtrack search algorithm to treat the orbit as if it starts from the specified latitude. This is optional and will default to 0 if not specified.", + "type": "number" + }, + "StartCircularLatitudeUnit": { + "description": "The StartCircularLatitude value's unit.", + "type": "string", + "enum": ["Degree"] + } + }, + "required": ["SwathWidth", "SwathWidthUnit", "Footprints", "OrbitPeriod", "OrbitPeriodUnit", "InclinationAngle", "InclinationAngleUnit", "NumberOfOrbits"], + "dependencies": { + "StartCircularLatitude": ["StartCircularLatitudeUnit"] + } + }] + }, + "GranuleSpatialRepresentationEnum": { + "type": "string", + "enum": ["CARTESIAN", "GEODETIC", "ORBIT", "NO_SPATIAL"] + }, + "OrbitParameterExistsIfGranuleSpatialRepresentationIsOrbit": { + "$comment": "Checks if the Granule Spatial Representation value is Oribt, then the oribt parameter must exist.", + "if": {"properties": {"GranuleSpatialRepresentation": {"const": "ORBIT"}}}, + "then": {"required": ["OrbitParameters"]} + }, + "ResolutionAndCoordinateSystemType": { + "description": "This class defines the horizontal spatial extents coordinate system and the data product's horizontal data resolution. The horizontal data resolution is defined as the smallest horizontal distance between successive elements of data in a dataset. This is synonymous with terms such as ground sample distance, sample spacing and pixel size. It is to be noted that the horizontal data resolution could be different in the two horizontal dimensions. Also, it is different from the spatial resolution of an instrument, which is the minimum distance between points that an instrument can see as distinct.", + "oneOf": [{ + "type": "object", + "title": "Description of the Resolution", + "additionalProperties": false, + "properties": { + "Description": { + "description": "This element holds a description about the resolution and coordinate system for people to read.", + "$ref": "#/definitions/DescriptionType" + }, + "GeodeticModel": { + "description": "This element describes the geodetic model for the data product.", + "$ref": "#/definitions/GeodeticModelType" + } + }, + "required": ["GeodeticModel"] + }, { + "type": "object", + "title": "Horizontal Data Resolution Information", + "additionalProperties": false, + "properties": { + "Description": { + "description": "This element holds a description about the resolution and coordinate system for people to read.", + "$ref": "#/definitions/DescriptionType" + }, + "GeodeticModel": { + "description": "This element describes the geodetic model for the data product.", + "$ref": "#/definitions/GeodeticModelType" + }, + "HorizontalDataResolution": { + "description": "This class defines a number of the data products horizontal data resolution. The horizontal data resolution is defined as the smallest horizontal distance between successive elements of data in a dataset. This is synonymous with terms such as ground sample distance, sample spacing and pixel size. It is to be noted that the horizontal data resolution could be different in the two horizontal dimensions. Also, it is different from the spatial resolution of an instrument, which is the minimum distance between points that an instrument can see as distinct.", + "$ref": "#/definitions/HorizontalDataResolutionType" + } + }, + "required": ["HorizontalDataResolution"] + }, { + "type": "object", + "title": "Local Coordinate System Information", + "additionalProperties": false, + "properties": { + "Description": { + "description": "This element holds a description about the resolution and coordinate system for people to read.", + "$ref": "#/definitions/DescriptionType" + }, + "GeodeticModel": { + "description": "This element describes the geodetic model for the data product.", + "$ref": "#/definitions/GeodeticModelType" + }, + "LocalCoordinateSystem": { + "description": "This element describes the local coordinate system for the data product.", + "$ref": "#/definitions/LocalCoordinateSystemType" + } + }, + "required": ["LocalCoordinateSystem"] + }] + }, + "DescriptionType": { + "description": "This element defines what a description is.", + "type": "string", + "minLength": 1, + "maxLength": 2048 + }, + "GeodeticModelType": { + "description": "This element describes the geodetic model for the data product.", + "type": "object", + "additionalProperties": false, + "properties": { + "HorizontalDatumName": { + "description": "The identification given to the reference system used for defining the coordinates of points.", + "$ref": "#/definitions/DatumNameType" + }, + "EllipsoidName": { + "description": "Identification given to established representation of the Earth's shape.", + "type": "string", + "minLength": 1, + "maxLength": 255 + }, + "SemiMajorAxis": { + "description": "Radius of the equatorial axis of the ellipsoid.", + "type": "number" + }, + "DenominatorOfFlatteningRatio": { + "description": "The ratio of the Earth's major axis to the difference between the major and the minor.", + "type": "number" + } + } + }, + "DatumNameType": { + "description": "The identification given to the level surface taken as the surface of reference from which measurements are compared.", + "type": "string", + "minLength": 1, + "maxLength": 80 + }, + "HorizontalDataResolutionType": { + "description": "This class defines a number of the data products horizontal data resolution. The horizontal data resolution is defined as the smallest horizontal distance between successive elements of data in a dataset. This is synonymous with terms such as ground sample distance, sample spacing and pixel size. It is to be noted that the horizontal data resolution could be different in the two horizontal dimensions. Also, it is different from the spatial resolution of an instrument, which is the minimum distance between points that an instrument can see as distinct.", + "type": "object", + "additionalProperties": false, + "properties": { + "VariesResolution": { + "description": "Varies Resolution object describes a data product that has a number of resolution values.", + "$ref": "#/definitions/HorizontalDataResolutionVariesType" + }, + "PointResolution": { + "description": "Point Resolution object describes a data product that is from a point source.", + "$ref": "#/definitions/HorizontalDataResolutionPointType" + }, + "NonGriddedResolutions": { + "description": "Non Gridded Resolutions object describes resolution data for non gridded data products.", + "type": "array", + "items": { + "$ref": "#/definitions/HorizontalDataResolutionNonGriddedType" + }, + "minItems": 1 + }, + "NonGriddedRangeResolutions": { + "description": "Non Gridded Range Resolutions object describes range resolution data for non gridded data products.", + "type": "array", + "items": { + "$ref": "#/definitions/HorizontalDataResolutionNonGriddedRangeType" + }, + "minItems": 1 + }, + "GriddedResolutions": { + "description": "Gridded Resolutions object describes resolution data for gridded data products.", + "type": "array", + "items": { + "$ref": "#/definitions/HorizontalDataResolutionGriddedType" + }, + "minItems": 1 + }, + "GriddedRangeResolutions": { + "description": "Gridded Range Resolutions object describes range resolution data for gridded data products.", + "type": "array", + "items": { + "$ref": "#/definitions/HorizontalDataResolutionGriddedRangeType" + }, + "minItems": 1 + }, + "GenericResolutions": { + "description": "Generic Resolutions object describes general resolution data for a data product where it is not known if a data product is gridded or not.", + "type": "array", + "items": { + "$ref": "#/definitions/HorizontalDataGenericResolutionType" + }, + "minItems": 1 + } + } + }, + "HorizontalDataResolutionVariesType": { + "description": "Varies Resolution object describes a data product that has a number of resolution values.", + "type": "string", + "enum": ["Varies"] + }, + "HorizontalDataResolutionPointType": { + "description": "Point Resolution object describes a data product that is from a point source.", + "type": "string", + "enum": ["Point"] + }, + "HorizontalDataResolutionNonGriddedType": { + "description": "Non Gridded Resolutions object describes resolution data for non gridded data products.", + "type": "object", + "additionalProperties": false, + "properties": { + "XDimension": { + "description": "The minimum difference between two adjacent values on a horizontal plane in the X axis. In most cases this is along the longitudinal axis.", + "type": "number" + }, + "YDimension": { + "description": "The minimum difference between two adjacent values on a horizontal plan in the Y axis. In most cases this is along the latitudinal axis.", + "type": "number" + }, + "Unit": { + "description": "Units of measure used for the XDimension and YDimension values.", + "$ref": "#/definitions/HorizontalDataResolutionUnitEnum" + }, + "ViewingAngleType": { + "description": "This element describes the angle of the measurement with respect to the instrument that gives an understanding of the specified resolution.", + "$ref": "#/definitions/HorizontalResolutionViewingAngleType" + }, + "ScanDirection": { + "description": "This element describes the instrument scanning direction.", + "$ref": "#/definitions/HorizontalResolutionScanDirectionType" + } + }, + "anyOf": [{ + "required": ["XDimension", "Unit"] + }, { + "required": ["YDimension", "Unit"] + }] + }, + "HorizontalDataResolutionNonGriddedRangeType": { + "description": "Non Gridded Range Resolutions object describes range resolution data for non gridded data products.", + "type": "object", + "additionalProperties": false, + "properties": { + "MinimumXDimension": { + "description": "The minimum, minimum difference between two adjacent values on a horizontal plane in the X axis. In most cases this is along the longitudinal axis.", + "type": "number" + }, + "MinimumYDimension": { + "description": "The minimum, minimum difference between two adjacent values on a horizontal plan in the Y axis. In most cases this is along the latitudinal axis.", + "type": "number" + }, + "MaximumXDimension": { + "description": "The maximum, minimum difference between two adjacent values on a horizontal plane in the X axis. In most cases this is along the longitudinal axis.", + "type": "number" + }, + "MaximumYDimension": { + "description": "The maximum, minimum difference between two adjacent values on a horizontal plan in the Y axis. In most cases this is along the latitudinal axis.", + "type": "number" + }, + "Unit": { + "description": "Units of measure used for the XDimension and YDimension values.", + "$ref": "#/definitions/HorizontalDataResolutionUnitEnum" + }, + "ViewingAngleType": { + "description": "This element describes the angle of the measurement with respect to the instrument that gives an understanding of the specified resolution.", + "$ref": "#/definitions/HorizontalResolutionViewingAngleType" + }, + "ScanDirection": { + "description": "This element describes the instrument scanning direction.", + "$ref": "#/definitions/HorizontalResolutionScanDirectionType" + } + }, + "anyOf": [{ + "required": ["MinimumXDimension", "MaximumXDimension", "Unit"] + }, { + "required": ["MinimumYDimension", "MaximumYDimension", "Unit"] + }] + }, + "HorizontalDataResolutionGriddedType": { + "description": "Gridded Resolutions object describes resolution data for gridded data products.", + "type": "object", + "additionalProperties": false, + "properties": { + "XDimension": { + "description": "The minimum difference between two adjacent values on a horizontal plane in the X axis. In most cases this is along the longitudinal axis.", + "type": "number" + }, + "YDimension": { + "description": "The minimum difference between two adjacent values on a horizontal plan in the Y axis. In most cases this is along the latitudinal axis.", + "type": "number" + }, + "Unit": { + "description": "Units of measure used for the XDimension and YDimension values.", + "$ref": "#/definitions/HorizontalDataResolutionUnitEnum" + } + }, + "anyOf": [{ + "required": ["XDimension", "Unit"] + }, { + "required": ["YDimension", "Unit"] + }] + }, + "HorizontalDataResolutionGriddedRangeType": { + "description": "Gridded Range Resolutions object describes range resolution data for gridded data products.", + "type": "object", + "additionalProperties": false, + "properties": { + "MinimumXDimension": { + "description": "The minimum, minimum difference between two adjacent values on a horizontal plane in the X axis. In most cases this is along the longitudinal axis.", + "type": "number" + }, + "MinimumYDimension": { + "description": "The minimum, minimum difference between two adjacent values on a horizontal plan in the Y axis. In most cases this is along the latitudinal axis.", + "type": "number" + }, + "MaximumXDimension": { + "description": "The maximum, minimum difference between two adjacent values on a horizontal plane in the X axis. In most cases this is along the longitudinal axis.", + "type": "number" + }, + "MaximumYDimension": { + "description": "The maximum, minimum difference between two adjacent values on a horizontal plan in the Y axis. In most cases this is along the latitudinal axis.", + "type": "number" + }, + "Unit": { + "description": "Units of measure used for the XDimension and YDimension values.", + "$ref": "#/definitions/HorizontalDataResolutionUnitEnum" + } + }, + "anyOf": [{ + "required": ["MinimumXDimension", "MaximumXDimension", "Unit"] + }, { + "required": ["MinimumYDimension", "MaximumYDimension", "Unit"] + }] + }, + "HorizontalDataGenericResolutionType": { + "description": "Generic Resolutions object describes general resolution data for a data product where it is not known if a data product is gridded or not.", + "type": "object", + "additionalProperties": false, + "properties": { + "XDimension": { + "description": "The minimum difference between two adjacent values on a horizontal plane in the X axis. In most cases this is along the longitudinal axis.", + "type": "number" + }, + "YDimension": { + "description": "The minimum difference between two adjacent values on a horizontal plan in the Y axis. In most cases this is along the latitudinal axis.", + "type": "number" + }, + "Unit": { + "description": "Units of measure used for the XDimension and YDimension values.", + "$ref": "#/definitions/HorizontalDataResolutionUnitEnum" + } + }, + "anyOf": [{ + "required": ["XDimension", "Unit"] + }, { + "required": ["YDimension", "Unit"] + }] + }, + "HorizontalDataResolutionUnitEnum": { + "description": "Units of measure used for the geodetic latitude and longitude resolution values (e.g., decimal degrees).", + "type": "string", + "enum": ["Decimal Degrees", "Kilometers", "Meters", "Statute Miles", "Nautical Miles", "Not provided"] + }, + "HorizontalResolutionViewingAngleType": { + "description": "This element describes the angle of the measurement with respect to the instrument that give an understanding of the specified resolution.", + "type": "string", + "enum": ["At Nadir", "Scan Extremes"] + }, + "HorizontalResolutionScanDirectionType": { + "description": "This element describes the instrument scanning direction.", + "type": "string", + "enum": ["Along Track", "Cross Track"] + }, + "LocalCoordinateSystemType": { + "type": "object", + "additionalProperties": false, + "properties": { + "GeoReferenceInformation": { + "description": "The information provided to register the local system to the Earth (e.g. control points, satellite ephemeral data, and inertial navigation data).", + "type": "string", + "minLength": 1, + "maxLength": 2048 + }, + "Description": { + "description": "A description of the Local Coordinate System and geo-reference information.", + "type": "string", + "minLength": 1, + "maxLength": 2048 + } + } + }, + "TemporalExtentType": { + "type": "object", + "additionalProperties": false, + "description": "Information which describes the temporal range or extent of a specific collection.", + "properties": { + "PrecisionOfSeconds": { + "description": "The precision (position in number of places to right of decimal point) of seconds used in measurement.", + "type": "integer" + }, + "EndsAtPresentFlag": { + "description": "Setting the Ends At Present Flag to 'True' indicates that a data collection which covers, temporally, a discontinuous range, currently ends at the present date. Setting the Ends at Present flag to 'True' eliminates the need to continuously update the Range Ending Time for collections where granules are continuously being added to the collection inventory.", + "type": "boolean" + }, + "RangeDateTimes": { + "description": "Stores the start and end date/time of a collection.", + "type": "array", + "items": { + "$ref": "#/definitions/RangeDateTimeType" + }, + "minItems": 1 + }, + "SingleDateTimes": { + "type": "array", + "items": { + "format": "date-time", + "type": "string" + }, + "minItems": 1 + }, + "PeriodicDateTimes": { + "description": "Temporal information about a collection having granules collected at a regularly occurring period. Information includes the start and end dates of the period, duration unit and value, and cycle duration unit and value.", + "type": "array", + "items": { + "$ref": "#/definitions/PeriodicDateTimeType" + }, + "minItems": 1 + }, + "TemporalResolution": { + "$ref": "#/definitions/TemporalResolutionType" + } + }, + "oneOf": [{ + "required": ["RangeDateTimes"] + }, { + "required": ["SingleDateTimes"] + }, { + "required": ["PeriodicDateTimes"] + }] + }, + "TemporalResolutionType": { + "oneOf": [{ + "type": "object", + "title": "Contant or Varies Resolution", + "additionalProperties": false, + "description": "Describes the amount of time between measurements.", + "properties": { + "Unit": { + "description": "Describes a constant or varies temporal resolution.", + "type": "string", + "enum": ["Constant", "Varies"] + } + }, + "required": ["Unit"] + }, { + "type": "object", + "title": "Numerical Resolution", + "additionalProperties": false, + "description": "Describes the amount of time between measurements.", + "properties": { + "Value": { + "description": "The temporal resolution value.", + "type": "number" + }, + "Unit": { + "description": "Describes a constant or varies temporal resolution.", + "type": "string", + "enum": ["Second", "Minute", "Hour", "Day", "Week", "Month", "Year", "Diurnal"] + } + }, + "required": ["Value", "Unit"] + }] + }, + "RangeDateTimeType": { + "type": "object", + "additionalProperties": false, + "description": "Stores the start and end date/time of a collection.", + "properties": { + "BeginningDateTime": { + "description": "The time when the temporal coverage period being described began.", + "format": "date-time", + "type": "string" + }, + "EndingDateTime": { + "description": "The time when the temporal coverage period being described ended.", + "format": "date-time", + "type": "string" + } + }, + "required": ["BeginningDateTime"] + }, + "PeriodicDateTimeType": { + "type": "object", + "additionalProperties": false, + "description": "Information about Periodic Date Time collections, including the name of the temporal period in addition to the start and end dates, duration unit and value, and cycle duration unit and value.", + "properties": { + "Name": { + "description": "The name given to the recurring time period. e.g. 'spring - north hemi.'", + "type": "string", + "minLength": 1, + "maxLength": 30 + }, + "StartDate": { + "description": "The date (day and time) of the first occurrence of this regularly occurring period which is relevant to the collection coverage.", + "format": "date-time", + "type": "string" + }, + "EndDate": { + "description": "The date (day and time) of the end occurrence of this regularly occurring period which is relevant to the collection coverage.", + "format": "date-time", + "type": "string" + }, + "DurationUnit": { + "description": "The unit specification for the period duration.", + "$ref": "#/definitions/DurationUnitEnum" + }, + "DurationValue": { + "description": "The number of PeriodDurationUnits in the RegularPeriodic period. e.g. the RegularPeriodic event 'Spring-North Hemi' might have a PeriodDurationUnit='MONTH' PeriodDurationValue='3' PeriodCycleDurationUnit='YEAR' PeriodCycleDurationValue='1' indicating that Spring-North Hemi lasts for 3 months and has a cycle duration of 1 year. The unit for the attribute is the value of the attribute PeriodDurationValue.", + "type": "integer" + }, + "PeriodCycleDurationUnit": { + "description": "The unit specification of the period cycle duration.", + "$ref": "#/definitions/DurationUnitEnum" + }, + "PeriodCycleDurationValue": { + "type": "integer" + } + }, + "required": ["Name", "StartDate", "EndDate", "DurationUnit", "DurationValue", "PeriodCycleDurationUnit", "PeriodCycleDurationValue"] + }, + "DurationUnitEnum": { + "type": "string", + "enum": ["DAY", "MONTH", "YEAR"] + } + } +} + + \ No newline at end of file diff --git a/schemas/resources/schemas/visualization/v1.0.0/umm-vis-maps-schema.json b/schemas/resources/schemas/visualization/v1.0.0/umm-vis-maps-schema.json new file mode 100644 index 0000000000..e9e8016b07 --- /dev/null +++ b/schemas/resources/schemas/visualization/v1.0.0/umm-vis-maps-schema.json @@ -0,0 +1,34 @@ +{ "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "What": { + "title": "UMM-Vis-Maps", + "type": "object", + "additionalProperties": false, + "description": "Specifies the Map type.", + "properties": { + "whatmap1": { + "type": "string" + }, + "whatmap2": { + "type": "string" + } + }, + "required": ["whatmap1", "whatmap2"] + }, + "How": { + "title": "UMM-Vis-Maps", + "type": "object", + "additionalProperties": false, + "description": "Specifies the Map type.", + "properties": { + "howmap1": { + "type": "string" + }, + "howmap2": { + "type": "string" + } + }, + "required": ["howmap1", "howmap2"] + } + } +} diff --git a/schemas/resources/schemas/visualization/v1.0.0/umm-vis-tiles-schema.json b/schemas/resources/schemas/visualization/v1.0.0/umm-vis-tiles-schema.json new file mode 100644 index 0000000000..571844ac24 --- /dev/null +++ b/schemas/resources/schemas/visualization/v1.0.0/umm-vis-tiles-schema.json @@ -0,0 +1,186 @@ +{ + "$id": "https://example.com/umm-vis-tiles.schema.json", + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "UMM-Vis-Tiles", + "definitions": { + "What": { + "type": "object", + "additionalProperties": false, + "description": "what this vis is.", + "properties": { + + "SupportsContinuousBrowsing": { + "description": "A boolean flag indicating whether the visualization layer supports “continuous browsing” across the anti-meridian.", + "type": "boolean" + }, + + "Dimension": { + "description": "dimension", + "type": "object" + }, + "Format": { + "description": "layer format", + "type": "string" + }, + "LegendURL": { + "description": "legend url", + "type": "array" + }, + "ResourceURL": { + "description": "resource url", + "type": "object" + }, + "Metadata": { + "description": "Metadata", + "type": "array" + }, + "WGS84BoundingBox": { + "description": "WGS84 Bounding Box", + "type": "object" + }, + "TileMatrixSetLink": { + "description": "tile matrix set link", + "type": "object", + "properties": { + "TileMatrixSet": { + "type": "string" + } + } + }, + + "measurement": { + "description": "The visualization layer’s measurement category", + "type": "string" + }, + "layerPeriod": { + "description": "The visualization layer’s temporal resolution", + "type": "string", + "enum": [ + "Subdaily", + "Daily", + "Multi-Day", + "4-Day", + "5-Day", + "7-Day", + "8-Day", + "16-Day", + "Weekly", + "Monthly", + "3-Month", + "Yearly" + ] + }, + "transAntiMeridian": { + "description": "Whether, and how, the visualization layer provides representations that cross the antimeridian.", + "type": "boolean" + }, + "daynight": { + "description": "Whether the visualization layer represents data captured during the day, night (or both) as perceived during time of data acquisition.", + "type": "array", + "uniqueItems": true, + "maxItems": 2, + "items": { + "enum": [ + "day", + "night" + ] + } + }, + "orbitTracks": { + "description": "Corresponding orbit track layers", + "type": "array", + "uniqueItems": true, + "items": { + "description": "Orbit track layer identifier(s)", + "type": "string", + "minLength": 1 + } + }, + "orbitDirection": { + "description": "Whether the visualization layer represents data from the ascending, descending, or both tracks of a satellite.", + "type": "array", + "maxItems": 2, + "items": { + "description": "Orbit track direction", + "enum": [ + "ascending", + "descending" + ] + } + }, + + "RetentionPeriod": { + "description": "TBD", + "type": "number" + }, + + "Ongoing": { + "description": "A boolean flag indicating whether the visualization layer. ", + "type": "boolean" + } + + } + + }, + + "How": { + + "type": "object", + "additionalProperties": false, + "description": "what this vis is.", + "properties": { + + "sourceData": { + "description": "source data used to create visualization", + "type": "array" + }, + + "projection": { + "description": "how reprojection is done. what source projection and output projection are", + "type": "object" + }, + "regridding": { + "description": "how regridding is done. what source grid and output grid are", + "type": "object" + }, + "sampling": { + "description": "interpolate or average out", + "type": "object" + }, + "resolution": { + "description": "resolution of source data and resulting visualization", + "type": "object" + }, + "qualityFlag": { + "description": "data value filtering, mask, etc.", + "type": "object" + }, + + "colorMap": { + "description": "color table", + "type": "object" + }, + "range": { + "description": "min/max value", + "type": "object" + }, + "scale": { + "description": "linear, logarithmic, etc.", + "type": "object" + }, + "pixelStyle": { + "description": "shape of pixel when visualization is rendered", + "type": "object" + } + + } + + }, + + "graphics": { + } + + }, + + "required": ["What", "How"] +} \ No newline at end of file diff --git a/search-app/src/cmr/search/services/parameters/conversion.clj b/search-app/src/cmr/search/services/parameters/conversion.clj index 6c973023b2..5af84ba62c 100644 --- a/search-app/src/cmr/search/services/parameters/conversion.clj +++ b/search-app/src/cmr/search/services/parameters/conversion.clj @@ -198,7 +198,7 @@ :concept-id :string :keyword :keyword}) -(doseq [concept-type cc/get-generic-non-draft-concept-types-array] +(doseq [concept-type (filterv #(not (string/includes? % "visualization")) cc/get-generic-non-draft-concept-types-array)] (defmethod common-params/param-mappings concept-type [_] {:name :string @@ -207,6 +207,19 @@ :native-id :string :concept-id :string})) +(defmethod common-params/param-mappings :visualization + [_] + {:name :string + :id :string + :identifier :string + :provider :string + :native-id :string + :concept-id :string + :visualization-type :string + :concept-ids :string + :keyword :string + :title :string}) + (doseq [concept-type cc/get-draft-concept-types-array] (defmethod common-params/param-mappings concept-type [_] @@ -254,25 +267,16 @@ "Set of granule search parameter names." (set (keys (common-params/param-mappings :granule)))) -(defmulti tag-param->condition +(defn tag-param->condition "Convert tag param and value into query condition" - (fn [param value pattern?] - param)) - -(defmethod tag-param->condition :tag-key - [param value pattern?] - (nf/parse-nested-condition :tags {param value} false pattern?)) - -(defmethod tag-param->condition :tag-originator-id - [param value pattern?] - (nf/parse-nested-condition :tags {:originator-id value} false pattern?)) - -(defmethod tag-param->condition :tag-data [param value pattern?] - (let [conditions (for [[tag-key tag-value] value] - (nf/parse-nested-condition :tags {:tag-key (name tag-key) - :tag-value tag-value} false pattern?))] - (gc/and-conds conditions))) + (case param + :tag-key (nf/parse-nested-condition :tags {param value} false pattern?) + :tag-originator-id (nf/parse-nested-condition :tags {:originator-id value} false pattern?) + :tag-data (let [conditions (for [[tag-key tag-value] value] + (nf/parse-nested-condition :tags {:tag-key (name tag-key) + :tag-value tag-value} false pattern?))] + (gc/and-conds conditions)))) (defmethod common-params/parameter->condition :tag-query [_context concept-type param value options] @@ -303,7 +307,7 @@ ;; Special case handler for concept-id. Concept id can refer to a granule or collection. ;; If it's a granule query with a collection concept id then we convert the parameter to :collection-concept-id (defmethod common-params/parameter->condition :granule-concept-id - [context concept-type param value options] + [context concept-type _param value options] (let [values (if (sequential? value) value [value]) {granule-concept-ids :granule collection-concept-ids :collection} (group-by (comp :concept-type cc/parse-concept-id) values) @@ -328,7 +332,7 @@ ;; This will find granules which either have explicitly specified a value ;; or have not specified any value for the field and inherit it from their parent collection. (defmethod common-params/parameter->condition :inheritance - [context concept-type param value options] + [context _concept-type param value options] (let [field-condition (common-params/parameter->condition context :collection param value options) exclude-collection (= "true" (get-in options [param :exclude-collection])) collection-cond (gc/and-conds @@ -341,7 +345,7 @@ collection-cond])))) (defmethod common-params/parameter->condition :updated-since - [_context concept-type param value options] + [_context _concept-type param value _options] (cqm/map->DateRangeCondition {:field param :start-date (parser/parse-datetime @@ -458,7 +462,7 @@ sort-key)))))) (defmethod common-params/parse-query-level-params :collection - [concept-type params] + [_concept-type params] (let [[params query-attribs] (common-params/default-parse-query-level-params :collection params lp/param-aliases) query-attribs (reverse-has-granules-sort query-attribs) @@ -510,7 +514,7 @@ util/remove-nil-keys)])) (defmethod common-params/parse-query-level-params :granule - [concept-type params] + [_concept-type params] (let [[params query-attribs] (common-params/default-parse-query-level-params :granule params lp/param-aliases) result-features (when (= "v2" (util/safe-lowercase (:include-facets params))) @@ -541,39 +545,11 @@ :result-features result-features :gran-specific-items-query? gran-specific-items-query?})])) -(defmethod common-params/parse-query-level-params :variable - [concept-type params] - (let [[params query-attribs] (common-params/default-parse-query-level-params - :variable params)] - [(dissoc params :all-revisions) - (merge query-attribs {:all-revisions? (= "true" (:all-revisions params))})])) - -(defmethod common-params/parse-query-level-params :service - [concept-type params] - (let [[params query-attribs] (common-params/default-parse-query-level-params - :service params)] - [(dissoc params :all-revisions) - (merge query-attribs {:all-revisions? (= "true" (:all-revisions params))})])) - -(defmethod common-params/parse-query-level-params :tool - [concept-type params] - (let [[params query-attribs] (common-params/default-parse-query-level-params - :tool params)] - [(dissoc params :all-revisions) - (merge query-attribs {:all-revisions? (= "true" (:all-revisions params))})])) - -(defmethod common-params/parse-query-level-params :subscription - [concept-type params] - (let [[params query-attribs] (common-params/default-parse-query-level-params - :subscription params)] - [(dissoc params :all-revisions) - (merge query-attribs {:all-revisions? (= "true" (:all-revisions params))})])) - -(doseq [concept-type-key (cc/get-generic-concept-types-array)] +(doseq [concept-type-key (conj (cc/get-generic-concept-types-array) :subscription :tool :service :variable)] (defmethod common-params/parse-query-level-params concept-type-key - [concept-type params] + [_concept-type params] (let [[params query-attribs] (common-params/default-parse-query-level-params - concept-type-key params)] + concept-type-key params)] [(dissoc params :all-revisions) (merge query-attribs {:all-revisions? (= "true" (:all-revisions params))})]))) diff --git a/search-app/test/cmr/search/test/unit/data/elastic_search_index_test.clj b/search-app/test/cmr/search/test/unit/data/elastic_search_index_test.clj index 9d5cfd8e8a..08ee6669e0 100644 --- a/search-app/test/cmr/search/test/unit/data/elastic_search_index_test.clj +++ b/search-app/test/cmr/search/test/unit/data/elastic_search_index_test.clj @@ -1,6 +1,6 @@ (ns cmr.search.test.unit.data.elastic-search-index-test (:require - [clojure.test :refer :all] + [clojure.test :refer [deftest is testing use-fixtures]] [cmr.common.hash-cache :as hash-cache] [cmr.common.services.search.query-model :as qm] [cmr.elastic-utils.search.es-index-name-cache :as idx-names-cache] @@ -29,6 +29,7 @@ :tag {:tags "1_tags"} :generic-grid {:generic-grid "1_generic_grid" :all-generic-grid-revisions "1_all_generic_grid_revisions"} :generic-data-quality-summary {:generic-data-quality-summary "1_generic_data_quality_summary" :all-generic-data-quality-summary-revisions "1_all_generic_data_quality_summary_revisions"} + :generic-visualization {:generic-visualization "1_generic_visualization" :all-generic-visualization-revisions "1_all_generic_visualization_revisions"} :collection {:collections-v2 "1_collections_v2" :all-collection-revisions "1_all_collection_revisions"} :subscription {:subscriptions "1_subscriptions" :all-subscription-revisions "1_all_subscription_revisions"} :rebalancing-collections ()}) diff --git a/search-app/test/cmr/search/test/unit/services/parameters_conversion.clj b/search-app/test/cmr/search/test/unit/services/parameters_conversion.clj new file mode 100644 index 0000000000..f098b32cee --- /dev/null +++ b/search-app/test/cmr/search/test/unit/services/parameters_conversion.clj @@ -0,0 +1,76 @@ +(ns cmr.search.test.unit.services.parameters-conversion + (:require + [clojure.test :refer [deftest is testing]] + [cmr.common.util :as util :refer [are3]] + [cmr.search.services.parameters.conversion :as param-conv])) + +;; Test for converting tag parameters +(deftest tag-parameter->query-condition-test + (testing "Testing the conversion of tag parameters to query condition." + (are3 + [expected-condition param value pattern?] + (let [actual (util/dissoc-multiple + (param-conv/tag-param->condition param value pattern?) + [:_type]) + expected (util/dissoc-multiple expected-condition [:_type])] + (is (= expected actual))) + + "Testing tag-key parameter" + #cmr.common.services.search.query_model.NestedCondition + {:path :tags + :condition #cmr.common.services.search.query_model.StringCondition + {:field :tags.tag-key + :value "some_value" + :case-sensitive? false + :pattern? "some_pattern"}} + :tag-key + "some_value" + "some_pattern" + + "Testing tag-originator-id parameter" + #cmr.common.services.search.query_model.NestedCondition + {:path :tags + :condition #cmr.common.services.search.query_model.StringCondition + {:field :tags.originator-id + :value "some_value" + :case-sensitive? false + :pattern? "some_pattern"}} + :tag-originator-id + "some_value" + "some_pattern" + + "Testing tag-data parameter" + #cmr.common.services.search.query_model.ConditionGroup + {:operation :and + :conditions (#cmr.common.services.search.query_model.NestedCondition + {:path :tags + :condition #cmr.common.services.search.query_model.ConditionGroup + {:operation :and + :conditions (#cmr.common.services.search.query_model.StringCondition + {:field :tags.tag-key + :value "tag-key" + :case-sensitive? false + :pattern? "some_pattern"} + #cmr.common.services.search.query_model.StringCondition + {:field :tags.tag-value + :value "some_key" + :case-sensitive? false + :pattern? "some_pattern"})}} + #cmr.common.services.search.query_model.NestedCondition + {:path :tags + :condition #cmr.common.services.search.query_model.ConditionGroup + {:operation :and + :conditions (#cmr.common.services.search.query_model.StringCondition + {:field :tags.tag-key + :value "tag-value" + :case-sensitive? false + :pattern? "some_pattern"} + #cmr.common.services.search.query_model.StringCondition + {:field :tags.tag-value + :value "some_value" + :case-sensitive? false + :pattern? "some_pattern"})}})} + :tag-data + {:tag-key "some_key" + :tag-value "some_value"} + "some_pattern"))) diff --git a/system-int-test/src/cmr/system_int_test/utils/generic_util.clj b/system-int-test/src/cmr/system_int_test/utils/generic_util.clj index 9b47e14370..954b901c6e 100644 --- a/system-int-test/src/cmr/system_int_test/utils/generic_util.clj +++ b/system-int-test/src/cmr/system_int_test/utils/generic_util.clj @@ -41,6 +41,11 @@ (slurp) (json/parse-string true))) +(def visualization (-> "schemas/visualization/v1.0.0/metadata.json" + (io/resource) + (slurp) + (json/parse-string true))) + (def variable-draft-without-private (-> "schemas/variable-draft/v1.0.0/metadata_with_private_data.json" (io/resource)