001/* 002 * GeoAPI - Java interfaces for OGC/ISO standards 003 * Copyright © 2018-2024 Open Geospatial Consortium, Inc. 004 * http://www.geoapi.org 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.opengis.test.dataset; 019 020import java.io.File; 021import java.net.URL; 022import java.net.URI; 023import java.net.URISyntaxException; 024import java.io.IOException; 025import java.io.InputStream; 026import java.io.FileOutputStream; 027 028import static org.junit.jupiter.api.Assertions.*; 029 030 031/** 032 * Provides access to small built-in test files. 033 * Data can be obtained as {@link URL}, {@link File}, {@link InputStream} or {@code byte[]} array. 034 * 035 * @author Martin Desruisseaux (Geomatys) 036 * @version 3.1 037 * @since 3.1 038 */ 039public enum TestData { 040 /** 041 * A two-dimensional netCDF file using a geographic CRS for global data over the world. 042 * The file contains temperature data from model analysis, without missing values. 043 * 044 * <ul> 045 * <li><b>Data type:</b> 16 bits signed integers (only the positive range is used)</li> 046 * <li><b>Coordinate Reference System:</b> two-dimensional geographic</li> 047 * <li><b>Geographic area:</b> world, with latitudes ranging from 90°S to 90°N and longitudes from 180°E to 180°W</li> 048 * <li><b>Size:</b> 73 × 73 cells in a file of 12.7 kilobytes</li> 049 * </ul> 050 * <table class="ogc"> 051 * <caption>Global attributes</caption> 052 * <tr><th>Name</th><th>Value</th></tr> 053 * <tr><td>{@code Conventions}</td><td>CF-1.4</td></tr> 054 * <tr><td>{@code Metadata_Conventions}</td><td>Unidata Dataset Discovery v1.0</td></tr> 055 * <tr><td>{@code title}</td><td>Test data from Sea Surface Temperature Analysis Model</td></tr> 056 * <tr><td>{@code purpose}</td><td>GeoAPI conformance tests</td></tr> 057 * <tr><td>{@code summary}</td><td>Global, two-dimensional model data</td></tr> 058 * <tr><td>{@code keywords}</td><td>{@literal EARTH SCIENCE > Oceans > Ocean Temperature > Sea Surface Temperature}</td></tr> 059 * <tr><td>{@code keywords_vocabulary}</td><td>GCMD Science Keywords</td></tr> 060 * <tr><td>{@code geospatial_lat_min}</td><td>-90.0</td></tr> 061 * <tr><td>{@code geospatial_lat_max}</td><td>90.0</td></tr> 062 * <tr><td>{@code geospatial_lon_min}</td><td>-180.0</td></tr> 063 * <tr><td>{@code geospatial_lon_max}</td><td>180.0</td></tr> 064 * <tr><td>{@code geospatial_vertical_min}</td><td>0.0</td></tr> 065 * <tr><td>{@code geospatial_vertical_max}</td><td>0.0</td></tr> 066 * <tr><td>{@code time_coverage_start}</td><td>2005-09-22T00:00</td></tr> 067 * <tr><td>{@code time_coverage_duration}</td><td>0.0</td></tr> 068 * <tr><td>{@code cdm_data_type}</td><td>Grid</td></tr> 069 * <tr><td>{@code id}</td><td>NCEP/SST/Global_5x2p5deg/SST_Global_5x2p5deg_20050922_0000.nc</td></tr> 070 * <tr><td>{@code naming_authority}</td><td>edu.ucar.unidata</td></tr> 071 * <tr><td>{@code creator_name}</td><td>NOAA/NWS/NCEP</td></tr> 072 * <tr><td>{@code date_created}</td><td>2005-09-22T00:00</td></tr> 073 * <tr><td>{@code date_modified}</td><td>2018-05-15T13:00</td></tr> 074 * <tr><td>{@code date_metadata_modified}</td><td>2018-05-15T13:01</td></tr> 075 * <tr><td>{@code history}</td><td>Decimated and modified by GeoAPI for inclusion in conformance test suite.</td></tr> 076 * <tr><td>{@code comment}</td><td>For testing purpose only.</td></tr> 077 * <tr><td>{@code license}</td><td>Freely available</td></tr> 078 * </table> 079 * 080 * In this file, all global attributes are character sequences, including the attributes that should 081 * be floating points numbers ({@code geospatial_lat_min}, {@code geospatial_lat_max}, <i>etc.</i>). 082 * Implementations are encouraged to be tolerant. 083 */ 084 NETCDF_2D_GEOGRAPHIC("Cube2D_geographic_packed.nc", 12988), 085 086 /** 087 * A four-dimensional netCDF file using a projected CRS with elevation and time. 088 * The file contains <cite>Current Icing Product</cite> data without missing values. 089 * The coordinate reference system also contains an height axis and a time axis. 090 * 091 * <ul> 092 * <li><b>Data type:</b> 32 bits floating point numbers</li> 093 * <li><b>Coordinate Reference System:</b> four-dimensional projected + elevation + temporal</li> 094 * <li><b>Geographic area:</b> East part of North America</li> 095 * <li><b>Size:</b> 38 × 19 × 4 × 1 cells in a file of 14.2 kilobytes</li> 096 * </ul> 097 * <table class="ogc"> 098 * <caption>Global attributes</caption> 099 * <tr><th>Name</th><th>Value</th></tr> 100 * <tr><td>{@code Conventions}</td><td>CF-1.4</td></tr> 101 * <tr><td>{@code title}</td><td>Test data from Current Icing Product (CIP)</td></tr> 102 * <tr><td>{@code purpose}</td><td>GeoAPI conformance tests</td></tr> 103 * <tr><td>{@code summary}</td><td>Hourly, three-dimensional diagnosis of the icing environment.</td></tr> 104 * <tr><td>{@code source}</td><td>U.S. National Weather Service - NCEP (WMC)</td></tr> 105 * <tr><td>{@code institution}</td><td>UCAR</td></tr> 106 * <tr><td>{@code topic_category}</td><td>climatology meteorology atmosphere</td></tr> 107 * <tr><td>{@code geospatial_lat_min}</td><td>15.94</td></tr> 108 * <tr><td>{@code geospatial_lat_max}</td><td>58.37</td></tr> 109 * <tr><td>{@code geospatial_lon_min}</td><td>-107.75</td></tr> 110 * <tr><td>{@code geospatial_lon_max}</td><td>-56.66</td></tr> 111 * <tr><td>{@code geospatial_vertical_min}</td><td>300.0</td></tr> 112 * <tr><td>{@code geospatial_vertical_max}</td><td>4875.0</td></tr> 113 * <tr><td>{@code geospatial_vertical_positive}</td><td>up</td></tr> 114 * <tr><td>{@code geospatial_lat_resolution}</td><td>0.93</td></tr> 115 * <tr><td>{@code geospatial_lon_resolution}</td><td>1.34</td></tr> 116 * <tr><td>{@code creator_name}</td><td>John Doe</td></tr> 117 * <tr><td>{@code creator_email}</td><td>john.doe@example.org</td></tr> 118 * <tr><td>{@code date_modified}</td><td>2012-02-21T21:14Z</td></tr> 119 * <tr><td>{@code date_metadata_modified}</td><td>2018-05-14T14:45Z</td></tr> 120 * <tr><td>{@code history}</td><td>Decimated and modified by GeoAPI for inclusion in conformance test suite.</td></tr> 121 * <tr><td>{@code comment}</td><td>For testing purpose only.</td></tr> 122 * </table> 123 * 124 * In this file, all global attributes for numeric values use the {@code float} type. 125 */ 126 NETCDF_4D_PROJECTED("Cube4D_projected_float.nc", 14544), 127 128 /** 129 * Trajectory of 3 features with 6, 4 and 3 points respectively, followed by two intentionally empty features. 130 * This is a small extract of {@code "JaPOPPO.csv"} file encoded as specified in 131 * <a href="http://docs.opengeospatial.org/bp/16-114r3/16-114r3.html">OGC 16-114</a> best practice paper. 132 * The two trailing empty features are for testing implementation capability to ignore them. 133 * Coordinates are latitude, longitude and time as minutes elapsed since 2014-11-29 midnight UTC. 134 * Features contain one property named "stations" defined as an enumeration: 135 * 136 * <pre>short stations(points): 137 * stations:flag_values = 11s, 12s, 13s, 14s, 15s, 16s, 21s, 22s, 23s, 24s, 31s, 32s 138 * stations:flag_meanings = "Yokohama Kawasaki Shinagawa Shinbashi Yurakucho Tokyo " 139 * "Shinjuku Yotsuya Ochanomizu Akihabara Koenji Nakano"</pre> 140 * 141 * <table class="ogc"> 142 * <caption>Global attributes</caption> 143 * <tr><th>Name</th><th>Value</th></tr> 144 * <tr><td>{@code Conventions}</td><td>CF-1.6</td></tr> 145 * <tr><td>{@code featureType}</td><td>trajectory</td></tr> 146 * <tr><td>{@code title}</td><td>Small moving features file</td></tr> 147 * <tr><td>{@code purpose}</td><td>GeoAPI conformance tests</td></tr> 148 * <tr><td>{@code source}</td><td>Extracts from JaPOPPO.csv</td></tr> 149 * <tr><td>{@code geospatial_lat_min}</td><td>30</td></tr> 150 * <tr><td>{@code geospatial_lat_max}</td><td>40</td></tr> 151 * <tr><td>{@code geospatial_lon_min}</td><td>130</td></tr> 152 * <tr><td>{@code geospatial_lon_max}</td><td>150</td></tr> 153 * <tr><td>{@code time_coverage_start}</td><td>2014-11-29T00:00:00Z</td></tr> 154 * <tr><td>{@code time_coverage_end}</td><td>2014-12-05T23:59:59Z</td></tr> 155 * <tr><td>{@code comment}</td><td>Intentionally contains two empty features for testing robustness.</td></tr> 156 * </table> 157 */ 158 MOVING_FEATURES("MovingFeatures.nc", 1932); 159 160 /** 161 * Name of the test file, located in the same directory (after JAR packaging) than the {@code TestData.class} file. 162 */ 163 private final String filename; 164 165 /** 166 * Expected length in bytes. 167 */ 168 private final int length; 169 170 /** 171 * Path to the (possibly temporary) file, or {@code null} if not yet created. 172 */ 173 private transient File file; 174 175 /** 176 * Creates a new enumeration value. 177 * 178 * @param filename name of the test file. 179 * @param length expected length in bytes. 180 */ 181 private TestData(final String filename, final int length) { 182 this.filename = filename; 183 this.length = length; 184 } 185 186 /** 187 * Returns a URL to the test file. 188 * The URL is not necessary a file on the default file system; it may be an entry inside a JAR file. 189 * If a path on the file system is desired, use {@link #file()} instead. 190 * 191 * @return a URL to the test file, possibly as an entry inside a JAR file. 192 */ 193 public URL location() { 194 final URL location = TestData.class.getResource(filename); 195 assertNotNull(location, filename); 196 return location; 197 } 198 199 /** 200 * Returns a path on the file system to the test file. If the test file is inside a JAR file, 201 * then it will be copied in a temporary directory and the path to the temporary file will be returned. 202 * 203 * @return a path on the default file system, possible as a temporary file. 204 * @throws IOException if a copy operation was necessary but failed. 205 */ 206 public synchronized File file() throws IOException { 207 if (file == null) { 208 final URI location; 209 try { 210 location = location().toURI(); 211 } catch (URISyntaxException e) { 212 throw new IOException(e); 213 } try { 214 file = new File(location); 215 } catch (IllegalArgumentException e) { 216 try { 217 final byte[] data = content(); 218 final File tmp = File.createTempFile("geoapi", filename.substring(filename.lastIndexOf('.'))); 219 tmp.deleteOnExit(); 220 try (FileOutputStream out = new FileOutputStream(tmp)) { 221 out.write(data); 222 } 223 file = tmp; // Set only on success. 224 } catch (IOException fe) { 225 fe.addSuppressed(e); 226 throw fe; 227 } 228 } 229 } 230 return file; 231 } 232 233 /** 234 * Opens an input stream on the test file. 235 * It is caller responsibility to close the stream after usage. 236 * 237 * @return an input stream on the test file. 238 */ 239 public InputStream open() { 240 final InputStream stream = TestData.class.getResourceAsStream(filename); 241 assertNotNull(stream, filename); 242 return stream; 243 } 244 245 /** 246 * Returns the full content of the test file as an array of bytes. 247 * 248 * @return the test file content. 249 * @throws IOException if an error occurred while reading the test file. 250 */ 251 public byte[] content() throws IOException { 252 /* 253 * We rely on the file having exactly the expected length, for avoiding array reallocations. 254 * This assumption is verified by the TestDataTest. 255 */ 256 final byte[] content = new byte[length]; 257 try (InputStream stream = open()) { 258 if (stream.readNBytes(content, 0, length) != length) { 259 throw new IOException("Unexpected file length."); 260 } 261 } 262 return content; 263 } 264}