diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/configuration/ElasticSearchConfig.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/configuration/ElasticSearchConfig.java index 85bbc85a..2254b1a6 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/core/configuration/ElasticSearchConfig.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/configuration/ElasticSearchConfig.java @@ -61,7 +61,7 @@ public ElasticsearchClient geoNetworkElasticsearchClient(RestClientTransport tra public Search createElasticSearch(ElasticsearchClient client, ObjectMapper mapper, @Value("${elasticsearch.index.name}") String indexName, - @Value("${elasticsearch.index.pageSize:2500}") Integer pageSize, + @Value("${elasticsearch.index.pageSize:2200}") Integer pageSize, @Value("${elasticsearch.search_as_you_type.size:10}") Integer searchAsYouTypeSize) { return new ElasticSearch(client, mapper, indexName, pageSize, searchAsYouTypeSize); diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/elastic/BBoxImpl.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/elastic/BBoxImpl.java index 24578636..a38265d0 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/elastic/BBoxImpl.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/elastic/BBoxImpl.java @@ -2,7 +2,9 @@ import au.org.aodn.ogcapi.server.core.model.enumeration.CQLCrsType; import au.org.aodn.ogcapi.server.core.model.enumeration.CQLFieldsInterface; +import au.org.aodn.ogcapi.server.core.util.GeometryUtils; import co.elastic.clients.elasticsearch._types.TopLeftBottomRightGeoBounds; +import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery; import org.geotools.filter.spatial.BBOXImpl; import org.geotools.geometry.jts.JTS; import org.geotools.geometry.jts.JTSFactoryFinder; @@ -11,6 +13,7 @@ import org.geotools.referencing.CRS; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.MultiLineString; import org.opengis.filter.FilterVisitor; import org.opengis.filter.MultiValuedFilter; import org.opengis.filter.expression.Expression; @@ -23,6 +26,7 @@ import org.opengis.referencing.FactoryException; import org.opengis.referencing.NoSuchAuthorityCodeException; import org.opengis.referencing.crs.CoordinateReferenceSystem; +import java.util.List; /** * This class support both 2D or 3D query, but now we just implement 2D and support very limited operation for CQL @@ -46,7 +50,7 @@ public BBoxImpl(Expression geometry, this.create3DCQL(geometry, box3D, matchAction); } else { - this.create2DCQL(geometry, bounds, matchAction, enumType); + this.create2DCQL(geometry, List.of(bounds), matchAction, enumType); } } @@ -86,8 +90,13 @@ public BBoxImpl( } else { crs = null; } + // This record the bounding box only, since the box may cross meridian, we need to split the polygon this.bounds = new ReferencedEnvelope(minx, maxx, miny, maxy, crs); - this.create2DCQL(e, bounds , matchAction, enumType); + + // We need to handle anti-meridian, we normalize the polygon and may split into two polygon to cover + // two area due to crossing -180 <> 180 line + Geometry g = GeometryUtils.normalizePolygon(GeometryUtils.createPolygon(minx, maxx, miny, maxy)); + this.create2DCQL(e, GeometryUtils.toReferencedEnvelope(g,crs) , matchAction, enumType); } catch (FactoryException fe) { throw new RuntimeException("Failed to setup bbox SRS", fe); @@ -97,17 +106,30 @@ public BBoxImpl( protected void create2DCQL( Expression geometry, - BoundingBox bounds, + List bounds, MultiValuedFilter.MatchAction matchAction, Class enumType) { this.matchAction = matchAction; this.geometry = geometry; - T v = Enum.valueOf(enumType, geometry.toString().toLowerCase()); - this.query = v.getBoundingBoxQuery( - TopLeftBottomRightGeoBounds.of(builder -> builder - .topLeft(i -> i.latlon(ll -> ll.lon(bounds.getMinX()).lat(bounds.getMaxY()))) - .bottomRight(i -> i.latlon(ll -> ll.lon(bounds.getMaxX()).lat(bounds.getMinY()))))); + final T v = Enum.valueOf(enumType, geometry.toString().toLowerCase()); + + if(bounds.size() > 1) { + // Handle multiple bounds by wrapping query with bool:should[] + this.query = BoolQuery.of(f -> f.should(bounds.stream().map(boundingBox -> v.getBoundingBoxQuery( + TopLeftBottomRightGeoBounds.of(builder -> builder + .topLeft(i -> i.latlon(ll -> ll.lon(boundingBox.getMinX()).lat(boundingBox.getMaxY()))) + .bottomRight(i -> i.latlon(ll -> ll.lon(boundingBox.getMaxX()).lat(boundingBox.getMinY()))) + ))) + .toList())) + ._toQuery(); + } + else { + this.query = v.getBoundingBoxQuery( + TopLeftBottomRightGeoBounds.of(builder -> builder + .topLeft(i -> i.latlon(ll -> ll.lon(bounds.get(0).getMinX()).lat(bounds.get(0).getMaxY()))) + .bottomRight(i -> i.latlon(ll -> ll.lon(bounds.get(0).getMaxX()).lat(bounds.get(0).getMinY()))))); + } } protected void create3DCQL(Expression geometry, BoundingBox3D bounds, MultiValuedFilter.MatchAction matchAction) { diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/service/ElasticSearchBase.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/service/ElasticSearchBase.java index e23675a9..f89dc969 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/core/service/ElasticSearchBase.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/service/ElasticSearchBase.java @@ -183,8 +183,7 @@ protected SearchResult searchCollectionBy(final List queries, }; try { - double random = Math.random(); - log.info("Start search {} {}", ZonedDateTime.now(), random); + log.info("Start search {} {}", ZonedDateTime.now(), Thread.currentThread().getName()); Iterable> response = pagableSearch(builderSupplier, ObjectNode.class, maxSize); SearchResult result = new SearchResult(); @@ -198,7 +197,7 @@ protected SearchResult searchCollectionBy(final List queries, lastSortValue = i.sort(); } } - log.info("End search {} {}", ZonedDateTime.now(), random); + log.info("End search {} {}", ZonedDateTime.now(), Thread.currentThread().getName()); // Return the last sort value if exist if(lastSortValue != null && !lastSortValue.isEmpty()) { List values = new ArrayList<>(); @@ -318,7 +317,7 @@ public Hit next() { protected StacCollectionModel formatResult(ObjectNode nodes) { try { if(nodes != null) { - String json = nodes.toPrettyString(); + String json = nodes.toString(); return mapper.readValue(json, StacCollectionModel.class); } else { diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/util/GeometryUtils.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/util/GeometryUtils.java index d366fde1..bec3476b 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/core/util/GeometryUtils.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/util/GeometryUtils.java @@ -7,12 +7,14 @@ import org.geotools.filter.LiteralExpressionImpl; import org.geotools.geojson.geom.GeometryJSON; import org.geotools.geometry.jts.JTSFactoryFinder; +import org.geotools.geometry.jts.ReferencedEnvelope; import org.locationtech.jts.geom.*; import org.locationtech.jts.io.ParseException; import org.locationtech.jts.io.WKTReader; import org.locationtech.spatial4j.context.jts.JtsSpatialContext; import org.locationtech.spatial4j.shape.jts.JtsGeometry; import org.opengis.referencing.FactoryException; +import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.TransformException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -236,4 +238,37 @@ public static Geometry createPolygon(double minx, double maxx, double miny, doub // Create the polygon (no holes) return factory.createPolygon(shell, null); } + + public static List toReferencedEnvelope(Geometry geometry, CoordinateReferenceSystem crs) { + List result = new ArrayList<>(); + if(geometry instanceof MultiPolygon mp) { + for(int i = 0; i < mp.getNumGeometries(); i++) { + if(mp.getGeometryN(i) instanceof Polygon lr) { + Coordinate[] coordinates = lr.getCoordinates(); + result.add(toReferencedEnvelope(coordinates, crs)); + } + } + } + else if(geometry instanceof Polygon p) { + result.add(toReferencedEnvelope(p.getCoordinates(), crs)); + } + return result; + } + + public static ReferencedEnvelope toReferencedEnvelope(Coordinate[] coordinates, CoordinateReferenceSystem crs) { + // Initialize bounds + double minx = Double.POSITIVE_INFINITY; + double maxx = Double.NEGATIVE_INFINITY; + double miny = Double.POSITIVE_INFINITY; + double maxy = Double.NEGATIVE_INFINITY; + + // Compute bounds + for (Coordinate coord : coordinates) { + minx = Math.min(minx, coord.x); + maxx = Math.max(maxx, coord.x); + miny = Math.min(miny, coord.y); + maxy = Math.max(maxy, coord.y); + } + return new ReferencedEnvelope(minx, maxx, miny, maxy, crs); + } } diff --git a/server/src/test/java/au/org/aodn/ogcapi/server/core/parser/stac/ParserTest.java b/server/src/test/java/au/org/aodn/ogcapi/server/core/parser/stac/ParserTest.java index d9c73f38..4bd00893 100644 --- a/server/src/test/java/au/org/aodn/ogcapi/server/core/parser/stac/ParserTest.java +++ b/server/src/test/java/au/org/aodn/ogcapi/server/core/parser/stac/ParserTest.java @@ -17,6 +17,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.MultiPolygon; import org.locationtech.jts.geom.Polygon; import org.locationtech.jts.io.ParseException; import org.opengis.filter.Filter; @@ -140,7 +141,7 @@ public void verifyBBoxWorks1() throws CQLException, IOException, FactoryExceptio * @throws ParseException - Will not throw */ @Test - public void verifyIntersectionWorks2() throws CQLException, IOException, FactoryException, TransformException, ParseException { + public void verifyIntersectionWorks2() throws CQLException, IOException { // Parse the json and get the noland section String json = BaseTestClass.readResourceFile("classpath:databag/0015db7e-e684-7548-e053-08114f8cd4ad.json"); @@ -177,7 +178,7 @@ public void verifyIntersectionWorks2() throws CQLException, IOException, Factory * @throws ParseException - Will not throw */ @Test - public void verifyBBoxWorks2() throws CQLException, IOException, FactoryException, TransformException, ParseException { + public void verifyBBoxWorks2() throws CQLException, IOException { // Parse the json and get the noland section String json = BaseTestClass.readResourceFile("classpath:databag/0015db7e-e684-7548-e053-08114f8cd4ad.json"); @@ -203,4 +204,46 @@ public void verifyBBoxWorks2() throws CQLException, IOException, FactoryExceptio Assertions.assertEquals(g.getCentroid().getX(), 168.30090846621448, 0.0000001, "getX()"); Assertions.assertEquals(g.getCentroid().getY(), -33.95984804960966, 0.0000001, "getY()"); } + /** + * Similar test as verifyBBoxWorks2, the BBOX not only cross meridian but the sample json have spatial extents + * near equator and span across the whole world + * + * @throws CQLException - Will not throw + * @throws IOException - Will not throw + * @throws FactoryException - Will not throw + * @throws TransformException - Will not throw + * @throws ParseException - Will not throw + */ + @Test + public void verifyBBoxWorks3() throws CQLException, IOException { + + // Parse the json and get the noland section + String json = BaseTestClass.readResourceFile("classpath:databag/c9055fe9-921b-44cd-b4f9-a00a1c93e8ac.json"); + StacCollectionModel model = mapper.readValue(json, StacCollectionModel.class); + + Filter filter = CompilerUtil.parseFilter( + Language.CQL, + "score>=1.5 AND BBOX(geometry,-209.8851491167079,-45.44715475181477,-149.06483661670887,-5.632766095762394)", + factory); + + Optional geo = GeometryUtils.readGeometry(model.getSummaries().getGeometryNoLand()); + + Assertions.assertTrue(geo.isPresent(), "Parse no land correct"); + GeometryVisitor visitor = GeometryVisitor.builder() + .build(); + + // return value are geo applied the CQL, and in this case only BBOX intersected + Geometry g = (Geometry)filter.accept(visitor, geo.get()); + + Assertions.assertTrue(g instanceof MultiPolygon); + + MultiPolygon mp = (MultiPolygon)g; + Assertions.assertEquals(mp.getNumGeometries(), 2, "Geometries correct"); + + Assertions.assertEquals(mp.getGeometryN(0).getCentroid().getX(), -159.53241830835444, 0.0000001, "getX() for 0"); + Assertions.assertEquals(mp.getGeometryN(0).getCentroid().getY(), -19.5, 0.0000001, "getY() for 0"); + + Assertions.assertEquals(mp.getGeometryN(1).getCentroid().getX(), 151.62121416760516, 0.0000001, "getX() for 1"); + Assertions.assertEquals(mp.getGeometryN(1).getCentroid().getY(), -18.000822620336752, 0.0000001, "getY() for 1"); + } } diff --git a/server/src/test/resources/databag/c9055fe9-921b-44cd-b4f9-a00a1c93e8ac.json b/server/src/test/resources/databag/c9055fe9-921b-44cd-b4f9-a00a1c93e8ac.json new file mode 100644 index 00000000..f6aff856 --- /dev/null +++ b/server/src/test/resources/databag/c9055fe9-921b-44cd-b4f9-a00a1c93e8ac.json @@ -0,0 +1,996 @@ +{ + "title": "Franklin Voyage FR 06/2001 Underway Data", + "description": "This dataset contains the Underway (UWY) data collected on Franklin voyage FR 06/2001. The voyage took place in the Pacific Ocean and the Coral sea between Western Samoa and Brisbane during July 2001. This dataset has been processed and archived within the CSIRO Marine and Atmospheric Research Data Centre in Hobart. Additional information regarding this dataset is contained in the cruise report for this voyage and/or the data processing report (as available). The standard Underway (=continuously recorded) dataset from a research voyage includes Navigation (NAV), Sounder, Thermosalinograph (TSG) and Meteorological (MET) data. NAV data includes GPS (Global Positioning System) measurements of latitude, longitude, ship's direction and speed. MET data includes atmospheric temperature, humidity and pressure, wind speed and direction, and incident radiation intensity. Data are recorded at or averaged over 5 minute intervals.", + "extent": { + "bbox": [ + [ + -170, + -27, + 153, + -12 + ], + [ + -170, + -27, + 153, + -12 + ] + ], + "temporal": [ + [ + "2001-07-07T14:00:00Z", + "2001-07-22T13:59:59Z" + ], + [ + "2001-07-07T14:00:00Z", + "2001-07-22T13:59:59Z" + ] + ] + }, + "summaries": { + "score": 90, + "status": "completed", + "credits": [ + "All Underway data sets processed by Bernadette Heaney" + ], + "statement": "Data source: original field data Data processing and quality control: according to current CMR Data Centre Underway processing manual GPS: The standard 1 minute positions have been archived and will be subsequently available in the underway file.
10 second values of latitude and longitude derived directly from the nmea strings will also be available in a netcdf file.
MET: For full details, refer to documentation.", + "revision": "2009-11-26T11:07:59", + "dataset_group": "csiro oceans and atmosphere", + "update_frequency": "completed", + "proj:geometry": { + "geometries": [ + { + "type": "Polygon", + "coordinates": [ + [ + [ + 153, + -27 + ], + [ + 153, + -12 + ], + [ + -170, + -12 + ], + [ + -170, + -27 + ], + [ + 153, + -27 + ] + ] + ] + } + ], + "type": "GeometryCollection" + }, + "proj:geometry_noland": { + "geometries": [ + { + "type": "Polygon", + "coordinates": [ + [ + [ + 32.5, + -27 + ], + [ + 113.5, + -27 + ], + [ + 113.25, + -26.25 + ], + [ + 113.75, + -26.5 + ], + [ + 113.5, + -25.5 + ], + [ + 114.25, + -26.25 + ], + [ + 113.5, + -24.5 + ], + [ + 113.75, + -22.5 + ], + [ + 116.75, + -20.5 + ], + [ + 121.25, + -19.5 + ], + [ + 123, + -16.5 + ], + [ + 123.5, + -17.5 + ], + [ + 123.5, + -16.25 + ], + [ + 125, + -16.5 + ], + [ + 124.5, + -15.5 + ], + [ + 125.25, + -15.5 + ], + [ + 126, + -14 + ], + [ + 127.5, + -14 + ], + [ + 128, + -15.5 + ], + [ + 128.5, + -14.75 + ], + [ + 129.75, + -15.25 + ], + [ + 129.25, + -14.25 + ], + [ + 130.5, + -12.5 + ], + [ + 132.75, + -12.25 + ], + [ + 132.5, + -12 + ], + [ + 131, + -12 + ], + [ + 49.25, + -12 + ], + [ + 40.5, + -12 + ], + [ + 40.5, + -15.5 + ], + [ + 34.5, + -19.5 + ], + [ + 35.5, + -24 + ], + [ + 32.5, + -26 + ], + [ + 32.5, + -27 + ] + ], + [ + [ + 48.75, + -13.5 + ], + [ + 49.25, + -12 + ], + [ + 50.5, + -15.5 + ], + [ + 49.75, + -15.5 + ], + [ + 49.75, + -16.75 + ], + [ + 47, + -25 + ], + [ + 45.25, + -25.5 + ], + [ + 44, + -25 + ], + [ + 43.25, + -22.25 + ], + [ + 44.25, + -20.5 + ], + [ + 44.5, + -16.25 + ], + [ + 46.5, + -16 + ], + [ + 48, + -14.75 + ], + [ + 48, + -13.5 + ], + [ + 48.75, + -13.5 + ] + ] + ] + }, + { + "type": "Polygon", + "coordinates": [ + [ + [ + -48.75, + -27 + ], + [ + 15.25, + -27 + ], + [ + 15, + -26.75 + ], + [ + 14.5, + -22.5 + ], + [ + 11.75, + -18 + ], + [ + 11.75, + -15.75 + ], + [ + 13.25, + -12 + ], + [ + -37.75, + -12 + ], + [ + -38.25, + -12.75 + ], + [ + -39, + -12.75 + ], + [ + -39.25, + -17.75 + ], + [ + -41, + -22 + ], + [ + -48.75, + -25.25 + ], + [ + -48.75, + -27 + ] + ] + ] + }, + { + "type": "Polygon", + "coordinates": [ + [ + [ + -170, + -27 + ], + [ + -70.75, + -27 + ], + [ + -70, + -20 + ], + [ + -70.25, + -18.5 + ], + [ + -71.5, + -17.25 + ], + [ + -76, + -14.75 + ], + [ + -77.25, + -12 + ], + [ + -170, + -12 + ], + [ + -170, + -27 + ] + ] + ] + }, + { + "type": "Polygon", + "coordinates": [ + [ + [ + 135.75, + -12 + ], + [ + 134.5, + -12 + ], + [ + 135.25, + -12.25 + ], + [ + 135.75, + -12 + ] + ] + ] + }, + { + "type": "Polygon", + "coordinates": [ + [ + [ + 136.5, + -12 + ], + [ + 136, + -12 + ], + [ + 136, + -12.5 + ], + [ + 136.5, + -12 + ] + ] + ] + }, + { + "type": "Polygon", + "coordinates": [ + [ + [ + 142.25, + -12 + ], + [ + 136.5, + -12 + ], + [ + 137, + -12.25 + ], + [ + 135.5, + -15 + ], + [ + 140.5, + -17.75 + ], + [ + 142.25, + -12 + ] + ] + ] + }, + { + "type": "Polygon", + "coordinates": [ + [ + [ + 153, + -12 + ], + [ + 143, + -12 + ], + [ + 143.75, + -14.5 + ], + [ + 145.25, + -15 + ], + [ + 146.25, + -19 + ], + [ + 148.75, + -20.25 + ], + [ + 149.75, + -22.5 + ], + [ + 150.75, + -22.25 + ], + [ + 150.75, + -23.5 + ], + [ + 153, + -25.25 + ], + [ + 153, + -12 + ] + ] + ] + } + ], + "type": "GeometryCollection" + }, + "temporal": [ + { + "start": "2001-07-07T14:00:00Z", + "end": "2001-07-22T13:59:59Z" + } + ], + "parameter_vocabs": [ + "salinity", + "depth", + "air pressure", + "temperature", + "humidity", + "air temperature", + "air-sea fluxes" + ], + "platform_vocabs": [ + "research vessel" + ] + }, + "contacts": [ + { + "roles": [ + "pointOfContact", + "about" + ], + "organization": "CSIRO Oceans & Atmosphere - Hobart", + "name": "CSIRO O&A, Information & Data Centre", + "position": "Data Requests", + "emails": [], + "addresses": [], + "phones": [], + "links": [] + }, + { + "roles": [ + "pointOfContact", + "metadata" + ], + "organization": "CSIRO Oceans & Atmosphere - Hobart", + "name": "CSIRO O&A, Information & Data Centre", + "position": "Data Requests", + "emails": [], + "addresses": [], + "phones": [], + "links": [] + } + ], + "languages": [ + { + "code": "eng", + "name": "English" + } + ], + "links": [ + { + "href": "https://www.cmar.csiro.au/geoserver/wms?&CQL_FILTER=SURVEY_NAME%20%3D%20%27FR200106%27", + "rel": "wms", + "type": "", + "title": "mnf:SHIPTRACK_GEOMETRY" + }, + { + "href": "https://www.marine.csiro.au/data/trawler/dataset.cfm?survey=FR200106&data_type=uwy", + "rel": "related", + "type": "", + "title": "Data Link" + }, + { + "href": "https://www.marine.csiro.au/data/trawler/download.cfm?file_id=2566", + "rel": "related", + "type": "text/html", + "title": "Data Link" + }, + { + "href": "https://www.marine.csiro.au/data/trawler/download.cfm?file_id=2567", + "rel": "related", + "type": "text/html", + "title": "Documentation Link" + }, + { + "href": "https://creativecommons.org/licenses/by/4.0/", + "rel": "related", + "type": "text/html", + "title": "Documentation Link" + }, + { + "href": "https://geonetwork-edge.edge.aodn.org.au:443/geonetwork/images/logos/58d5af30-fbc6-42dc-8971-5468df74e9ee.png", + "rel": "icon", + "type": "image/png", + "title": "Suggest icon for dataset" + }, + { + "href": "https://www.cmar.csiro.au/data/trawler/dataset_mapfile.cfm?survey=FR200106&data_type=uwy&img_type=tn", + "rel": "preview", + "type": "image" + } + ], + "license": "Data is made available under a Creative Commons Attribution 4.0 International Licence, please see link. Data is supplied 'as is' without any warranty or guarantee except as required by law to be given to you. The data may not be free of error, comprehensive, current or appropriate for your particular purpose. You accept all risk and responsibility for its use. ATTRIBUTION STATEMENT: The dataset [Insert-dataset-name-here] downloaded on [Insert-DD-Mmm-YYYY-here] was collected on voyage [Insert-voyage-name-here] by the Marine National Facility.", + "providers": [ + { + "name": "CSIRO Oceans & Atmosphere - Hobart", + "roles": [ + "pointOfContact" + ] + } + ], + "themes": [ + { + "concepts": [ + { + "id": "Earth Science | Atmosphere | Atmospheric Pressure | Atmospheric Pressure Measurements", + "url": "https://marlin.csiro.au/geonetwork/srv/eng/xml.keyword.get?thesaurus=external.theme.gcmd_keywords&id=http://gcmdservices.gsfc.nasa.gov/kms/concept/9efbc088-ba8c-4c9c-a458-ad6ad63f4188" + }, + { + "id": "Earth Science | Atmosphere | Atmospheric Radiation | Solar Radiation", + "url": "https://marlin.csiro.au/geonetwork/srv/eng/xml.keyword.get?thesaurus=external.theme.gcmd_keywords&id=http://gcmdservices.gsfc.nasa.gov/kms/concept/a0f3474e-9a54-4a82-97c4-43864b48df4c" + }, + { + "id": "Earth Science | Atmosphere | Atmospheric Temperature | Air Temperature", + "url": "https://marlin.csiro.au/geonetwork/srv/eng/xml.keyword.get?thesaurus=external.theme.gcmd_keywords&id=http://gcmdservices.gsfc.nasa.gov/kms/concept/f634ab55-de40-4d0b-93bc-691bf5408ccb" + }, + { + "id": "Earth Science | Atmosphere | Atmospheric Water Vapor | Humidity", + "url": "https://marlin.csiro.au/geonetwork/srv/eng/xml.keyword.get?thesaurus=external.theme.gcmd_keywords&id=http://gcmdservices.gsfc.nasa.gov/kms/concept/427e5121-a142-41cb-a8e9-a70b7f98eb6a" + }, + { + "id": "Earth Science | Atmosphere | Atmospheric Winds | Surface Winds", + "url": "https://marlin.csiro.au/geonetwork/srv/eng/xml.keyword.get?thesaurus=external.theme.gcmd_keywords&id=http://gcmdservices.gsfc.nasa.gov/kms/concept/10685919-bc01-43e7-901a-b62ac44627f3" + }, + { + "id": "Earth Science | Oceans | Bathymetry/Seafloor Topography | Water Depth", + "url": "https://marlin.csiro.au/geonetwork/srv/eng/xml.keyword.get?thesaurus=external.theme.gcmd_keywords&id=http://gcmdservices.gsfc.nasa.gov/kms/concept/ca477721-473b-40d7-a72b-4ffa963e48fb" + }, + { + "id": "Earth Science | Oceans | Ocean Temperature | Sea Surface Temperature", + "url": "https://marlin.csiro.au/geonetwork/srv/eng/xml.keyword.get?thesaurus=external.theme.gcmd_keywords&id=http://gcmdservices.gsfc.nasa.gov/kms/concept/bd24a9a9-7d52-4c29-b2a0-6cefd216ae78" + }, + { + "id": "Earth Science | Oceans | Salinity/Density | Salinity", + "url": "https://marlin.csiro.au/geonetwork/srv/eng/xml.keyword.get?thesaurus=external.theme.gcmd_keywords&id=http://gcmdservices.gsfc.nasa.gov/kms/concept/7e95b5fc-1d58-431a-af36-948b29fa870d" + } + ], + "scheme": "theme", + "description": "Olsen, L.M., G. Major, K. Shein, J. Scialdone, S. Ritz, T. Stevens, M. Morahan, A. Aleman, R. Vogel, S. Leicester, H. Weir, M. Meaux, S. Grebas, C.Solomon, M. Holland, T. Northcutt, R. A. Restrepo, R. Bilodeau, 2013. NASA/Global Change Master Directory (GCMD) Earth Science Keywords. Version 8.0.0.0.0", + "title": "GCMD Keywords" + }, + { + "concepts": [ + { + "id": "Meteorological Instruments", + "url": "https://marlin.csiro.au/geonetwork/srv/eng/xml.keyword.get?thesaurus=register.equipment.urn:marlin.csiro.au:Equipment&id=urn:marlin.csiro.au:Equipment:concept:61" + }, + { + "id": "Thermosalinographs", + "url": "https://marlin.csiro.au/geonetwork/srv/eng/xml.keyword.get?thesaurus=register.equipment.urn:marlin.csiro.au:Equipment&id=urn:marlin.csiro.au:Equipment:concept:101" + } + ], + "scheme": "equipment", + "description": "", + "title": "CSIRO Equipment" + }, + { + "concepts": [ + { + "id": "Global / Oceans | Pacific Ocean", + "url": "https://marlin.csiro.au/geonetwork/srv/eng/xml.keyword.get?thesaurus=register.place.urn:aodn.org.au:geographicextents&id=urn:aodn.org.au:geographicextents:concept:5" + }, + { + "id": "Regional Seas | Coral Sea", + "url": "https://marlin.csiro.au/geonetwork/srv/eng/xml.keyword.get?thesaurus=register.place.urn:aodn.org.au:geographicextents&id=urn:aodn.org.au:geographicextents:concept:13" + } + ], + "scheme": "place", + "description": "AODN GEN", + "title": "AODN Geographic Extent Names" + }, + { + "concepts": [ + { + "id": "Marine National Facility", + "url": "https://marlin.csiro.au/geonetwork/srv/eng/xml.keyword.get?thesaurus=register.discipline.urn:marlin.csiro.au:keywords:cmarAOI&id=urn:marlin.csiro.au:keywords:cmarAOI:concept:3208" + } + ], + "scheme": "discipline", + "description": "Marlin Areas of Interest Thesaurus", + "title": "CSIRO Areas Of Interest" + }, + { + "concepts": [ + { + "id": "Research Voyage: FR 06/2001", + "url": "https://marlin.csiro.au/geonetwork/srv/eng/xml.keyword.get?thesaurus=register.survey.urn:marlin.csiro.au:surveyregister&id=urn:marlin.csiro.au:surveyregister:concept:1175" + } + ], + "scheme": "survey", + "description": "MarLIN Survey Register", + "title": "CSIRO Survey List" + }, + { + "concepts": [ + { + "id": "Vessel Data: Underway", + "url": "https://marlin.csiro.au/geonetwork/srv/eng/xml.keyword.get?thesaurus=register.discipline.urn:marlin.csiro.au:keywords:standardDataType&id=urn:marlin.csiro.au:keywords:standardDataType:concept:3514" + } + ], + "scheme": "discipline", + "description": "MarLIN Standard Datatypes Thesaurus", + "title": "CSIRO Standard Data Types" + }, + { + "concepts": [ + { + "id": "Ship: Franklin", + "url": "https://marlin.csiro.au/geonetwork/srv/eng/xml.keyword.get?thesaurus=register.dataSource.urn:marlin.csiro.au:sourceregister&id=urn:marlin.csiro.au:sourceregister:concept:5" + } + ], + "scheme": "dataSource", + "description": "MarLIN Source Register", + "title": "CSIRO Source List" + }, + { + "concepts": [ + { + "id": "Practical salinity of the water body", + "url": "http://vocab.nerc.ac.uk/collection/P01/current/PSLTZZ01" + }, + { + "id": "Temperature of the water body", + "url": "http://vocab.nerc.ac.uk/collection/P01/current/TEMPPR01" + } + ], + "scheme": "", + "description": "", + "title": "Keywords (DataParameter)" + }, + { + "concepts": [ + { + "id": "thermosalinographs", + "url": "http://vocab.nerc.ac.uk/collection/L05/current/133" + } + ], + "scheme": "", + "description": "", + "title": "Keywords (Instrument)" + }, + { + "concepts": [ + { + "id": "research vessel", + "url": "http://vocab.nerc.ac.uk/collection/L06/current/31" + } + ], + "scheme": "", + "description": "", + "title": "Keywords (Platform)" + } + ], + "id": "c9055fe9-921b-44cd-b4f9-a00a1c93e8ac", + "search_suggestions": { + "abstract_phrases": [ + "been", + "averaged over", + "speed met data includes", + "includes gps global positioning", + "navigation nav sounder", + "radiation intensity data", + "global positioning system measurements", + "atmospheric research data", + "positioning system measurements", + "intensity data", + "uwy", + "voyage took", + "humidity", + "additional information", + "system measurements", + "wind speed", + "met data nav data", + "includes gps", + "july", + "pressure wind", + "atmospheric research data centre", + "contains", + "voyage fr", + "hobart additional information regarding", + "processing", + "underway continuously recorded", + "underway uwy", + "wind", + "atmospheric temperature humidity", + "dataset has", + "latitude", + "includes navigation nav sounder", + "global", + "sea", + "marine", + "continuously recorded", + "research voyage includes navigation", + "brisbane during", + "met data nav", + "archived", + "includes gps global", + "underway", + "coral sea between western", + "includes navigation", + "temperature", + "archived within", + "csiro", + "measurements", + "sounder thermosalinograph", + "data includes", + "pacific ocean", + "hobart additional", + "minute intervals", + "dataset from", + "information", + "dataset has been processed", + "dataset", + "standard", + "speed met", + "sounder", + "available", + "dataset contains", + "nav data includes gps", + "research", + "meteorological met data nav", + "incident radiation", + "nav sounder thermosalinograph", + "data includes gps global", + "has", + "positioning system", + "uwy data collected", + "latitude longitude", + "radiation intensity", + "sea between western", + "voyage took place", + "gps", + "regarding", + "standard underway", + "data collected", + "ocean", + "western", + "nav sounder", + "information regarding", + "coral sea between", + "research data", + "additional", + "coral sea", + "cruise report", + "fr", + "speed", + "voyage includes navigation", + "atmospheric research", + "csiro marine", + "brisbane", + "thermosalinograph tsg", + "averaged", + "between western samoa", + "voyage includes", + "continuously recorded dataset", + "over", + "data centre", + "recorded", + "underway uwy data collected", + "western samoa", + "data nav data", + "research data centre", + "gps global positioning", + "met data", + "sounder thermosalinograph tsg", + "data includes atmospheric temperature", + "data processing report", + "includes atmospheric temperature", + "collected", + "during", + "navigation nav", + "navigation", + "has been", + "hobart additional information", + "data nav data includes", + "between", + "nav", + "includes navigation nav", + "data nav", + "between western", + "research voyage includes", + "system", + "speed met data", + "meteorological met", + "temperature humidity", + "meteorological met data", + "nav data includes", + "underway uwy data", + "uwy data", + "underway continuously", + "meteorological", + "continuously recorded dataset from", + "sea between western samoa", + "place", + "nav sounder thermosalinograph tsg", + "recorded dataset", + "within", + "data processing", + "centre", + "continuously", + "pacific", + "global positioning system", + "samoa", + "report", + "met data includes atmospheric", + "processing report", + "thermosalinograph", + "standard underway continuously recorded", + "data", + "atmospheric temperature", + "includes atmospheric temperature humidity", + "hobart", + "incident radiation intensity data", + "gps global", + "incident radiation intensity", + "voyage includes navigation nav", + "coral", + "additional information regarding", + "from", + "dataset has been", + "longitude", + "data includes atmospheric", + "took", + "brisbane during july", + "franklin voyage", + "research voyage", + "includes", + "pressure", + "voyage", + "recorded dataset from", + "intervals", + "franklin voyage fr", + "gps global positioning system", + "cruise", + "franklin", + "been processed", + "tsg", + "nav data", + "atmospheric", + "has been processed", + "positioning", + "sea between", + "met data includes", + "met", + "direction", + "pressure wind speed", + "includes atmospheric", + "minute", + "standard underway continuously", + "intensity", + "processed", + "global positioning", + "contained", + "underway continuously recorded dataset", + "navigation nav sounder thermosalinograph", + "radiation", + "took place", + "during july", + "incident", + "data includes gps" + ], + "parameter_vocabs_sayt": [ + "temperature", + "salinity" + ], + "platform_vocabs_sayt": [ + "research vessel" + ] + }, + "sci:citation": "{\"suggestedCitation\":null,\"useLimitations\":null,\"otherConstraints\":[\"Data is made available under a Creative Commons Attribution 4.0 International Licence, please see link. Data is supplied 'as is' without any warranty or guarantee except as required by law to be given to you. The data may not be free of error, comprehensive, current or appropriate for your particular purpose. You accept all risk and responsibility for its use. ATTRIBUTION STATEMENT: The dataset [Insert-dataset-name-here] downloaded on [Insert-DD-Mmm-YYYY-here] was collected on voyage [Insert-voyage-name-here] by the Marine National Facility.\"]}", + "type": "Collection", + "stac_version": "1.0.0", + "stac_extensions": [ + "https://stac-extensions.github.io/scientific/v1.0.0/schema.json", + "https://stac-extensions.github.io/contacts/v0.1.1/schema.json", + "https://stac-extensions.github.io/projection/v1.1.0/schema.json", + "https://stac-extensions.github.io/language/v1.0.0/schema.json", + "https://stac-extensions.github.io/themes/v1.0.0/schema.json", + "https://stac-extensions.github.io/web-map-links/v1.2.0/schema.json" + ] +}