source: trunk/src/java/uk/ac/rdg/resc/ncwms/config/DefaultDataReader.java @ 903

Revision 903, 11.6 KB checked in by jonblower, 14 months ago (diff)

Added EPSG:3857 (Google Maps) to the list of supported CRSs in AbstractWmsController?

Line 
1/*
2 * Copyright (c) 2006 The University of Reading
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the University of Reading, nor the names of the
14 *    authors or contributors may be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29package uk.ac.rdg.resc.ncwms.config;
30
31import java.io.IOException;
32import java.util.Collection;
33import java.util.List;
34import org.slf4j.Logger;
35import org.slf4j.LoggerFactory;
36import ucar.nc2.dataset.NetcdfDataset;
37import uk.ac.rdg.resc.edal.cdm.CdmUtils;
38import uk.ac.rdg.resc.edal.coverage.CoverageMetadata;
39import uk.ac.rdg.resc.edal.coverage.domain.Domain;
40import uk.ac.rdg.resc.edal.geometry.HorizontalPosition;
41import uk.ac.rdg.resc.ncwms.util.WmsUtils;
42import uk.ac.rdg.resc.ncwms.wms.Layer;
43
44/**
45 * Default data reading class for CF-compliant NetCDF datasets.  Delegates most
46 * of its work to the {@link CdmUtils} class.
47 * @todo move the initialization of the NetcdfDataset cache to here?
48 *
49 * @author Jon Blower
50 */
51public class DefaultDataReader extends DataReader
52{
53    private static final Logger logger = LoggerFactory.getLogger(DefaultDataReader.class);
54
55    @Override
56    protected Collection<CoverageMetadata> readLayerMetadata(String location)
57            throws IOException
58    {
59        NetcdfDataset nc = null;
60        try
61        {
62            // Open the dataset, using the cache for NcML aggregations
63            nc = openDataset(location);
64            // Read and return the metadata
65            return CdmUtils.readCoverageMetadata(CdmUtils.getGridDataset(nc));
66        }
67        finally
68        {
69            closeDataset(nc);
70        }
71    }
72
73    /**
74     * Reads data from a NetCDF file.  Reads data for a single timestep only.
75     * This method knows
76     * nothing about aggregation: it simply reads data from the given file.
77     * Missing values (e.g. land pixels in oceanography data) will be represented
78     * by null.
79     *
80     * @param filename Location of the file, NcML aggregation or OPeNDAP URL
81     * @param layer {@link Layer} object representing the variable
82     * @param tIndex The index along the time axis (or -1 if there is no time axis)
83     * @param zIndex The index along the vertical axis (or -1 if there is no vertical axis)
84     * @param domain The list of real-world x-y points for which we need data.
85     * In the case of a GetMap operation this will usually be a {@link HorizontalGrid}.
86     * @return an array of floating-point data values, one for each point in
87     * the {@code pointList}, in the same order.
88     * @throws IOException if an input/output exception occurred when reading data
89     */
90    @Override
91    public List<Float> read(String filename, Layer layer, int tIndex, int zIndex,
92        Domain<HorizontalPosition> domain) throws IOException
93    {
94        NetcdfDataset nc = null;
95        try
96        {
97            // Open the dataset, using the cache for NcML aggregations
98            nc = openDataset(filename);
99            // Read and return the data
100            return CdmUtils.readHorizontalPoints(
101                nc,
102                layer.getId(),           // The grid of data to read from
103                layer.getHorizontalGrid(),
104                tIndex,
105                zIndex,
106                domain
107            );
108        }
109        finally
110        {
111            closeDataset(nc);
112        }
113    }
114
115    /**
116     * Reads data from a NetCDF file.  Reads data for a single timestep only.
117     * This method knows
118     * nothing about aggregation: it simply reads data from the given file.
119     * Missing values (e.g. land pixels in oceanography data) will be represented
120     * by null.
121     *
122     * @param filename Location of the file, NcML aggregation or OPeNDAP URL
123     * @param layer {@link Layer} object representing the variable
124     * @param tIndex The index along the time axis (or -1 if there is no time axis)
125     * @param zIndices The indices along the vertical axis.  If there is no
126     * vertical axis
127     * @param domain The list of real-world x-y points for which we need data.
128     * In the case of a GetMap operation this will usually be a {@link HorizontalGrid}.
129     * @return an array of floating-point data values, one for each point in
130     * the {@code pointList}, in the same order.
131     * @throws IOException if an input/output exception occurred when reading data
132     */
133    @Override
134    public List<List<Float>> readVerticalSection(String filename, Layer layer, int tIndex,
135        List<Integer> zIndices, Domain<HorizontalPosition> domain) throws IOException
136    {
137        NetcdfDataset nc = null;
138        try
139        {
140            // Open the dataset, using the cache for NcML aggregations
141            nc = openDataset(filename);
142            // Read and return the data
143            return CdmUtils.readVerticalSection(
144                nc,
145                layer.getId(),
146                layer.getHorizontalGrid(),
147                tIndex,
148                zIndices,
149                domain
150            );
151        }
152        finally
153        {
154            closeDataset(nc);
155        }
156    }
157
158    /**
159     * <p>Reads a timeseries of data from a file from a single xyz point.  This
160     * method knows nothing about aggregation: it simply reads data from the
161     * given file.  Missing values (e.g. land pixels in oceanography data) will
162     * be represented by null.</p>
163     * <p>If the provided Layer doesn't have a time axis then {@code tIndices}
164     * must be a single-element list with value -1.  In this case the returned
165     * "timeseries" of data will be a single data value. (TODO: make this more
166     * sensible.)</p>
167     * <p>This implementation reads all data with a single I/O operation
168     * (as opposed to the {@link DataReader#readTimeseries(java.lang.String,
169     * uk.ac.rdg.resc.ncwms.metadata.Layer, java.util.List, int,
170     * uk.ac.rdg.resc.ncwms.coordsys.LonLatPosition) superclass implementation},
171     * which uses an I/O operation for each individual point).  This method is
172     * therefore expected to be more efficient, particularly when reading from
173     * OPeNDAP servers.</p>
174     * @param filename Location of the file, NcML aggregation or OPeNDAP URL
175     * @param layer {@link Layer} object representing the variable
176     * @param tIndices the indices along the time axis within this file
177     * @param zIndex The index along the vertical axis (or -1 if there is no vertical axis)
178     * @param xy the horizontal position of the point
179     * @return an array of floating-point data values, one for each point in
180     * {@code tIndices}, in the same order.
181     * @throws IOException if an input/output exception occurred when reading data
182     * @todo Validity checking on tIndices and layer.hasTAxis()?
183     */
184    @Override
185    public List<Float> readTimeseries(String filename, Layer layer,
186        List<Integer> tIndices, int zIndex, HorizontalPosition xy)
187        throws IOException
188    {
189        NetcdfDataset nc = null;
190        try
191        {
192            // Open the dataset, using the cache for NcML aggregations
193            nc = openDataset(filename);
194            // Read and return the data
195            return CdmUtils.readTimeseries(
196                nc,
197                layer.getId(),
198                layer.getHorizontalGrid(),
199                tIndices,
200                zIndex,
201                xy
202            );
203        }
204        finally
205        {
206            closeDataset(nc);
207        }
208    }
209
210    /**
211     * Opens the NetCDF dataset at the given location, using the dataset
212     * cache if {@code location} represents an NcML aggregation.  We cannot
213     * use the cache for OPeNDAP or single NetCDF files because the underlying
214     * data may have changed and the NetcdfDataset cache may cache a dataset
215     * forever.  In the case of NcML we rely on the fact that server administrators
216     * ought to have set a "recheckEvery" parameter for NcML aggregations that
217     * may change with time.  It is desirable to use the dataset cache for NcML
218     * aggregations because they can be time-consuming to assemble and we don't
219     * want to do this every time a map is drawn.
220     * @param location The location of the data: a local NetCDF file, an NcML
221     * aggregation file or an OPeNDAP location, {@literal i.e.} anything that can be
222     * passed to NetcdfDataset.openDataset(location).
223     * @return a {@link NetcdfDataset} object for accessing the data at the
224     * given location.
225     * @throws IOException if there was an error reading from the data source.
226     */
227    private static NetcdfDataset openDataset(String location) throws IOException
228    {
229        boolean usedCache = false;
230        NetcdfDataset nc;
231        long start = System.nanoTime();
232        if (WmsUtils.isNcmlAggregation(location))
233        {
234            // We use the cache of NetcdfDatasets to read NcML aggregations
235            // as they can be time-consuming to put together.  If the underlying
236            // data can change we rely on the server admin setting the
237            // "recheckEvery" parameter in the aggregation file.
238            nc = NetcdfDataset.acquireDataset(location, null);
239            usedCache = true;
240        }
241        else
242        {
243            // For local single files and OPeNDAP datasets we don't use the
244            // cache, to ensure that we are always reading the most up-to-date
245            // data.  There is a small possibility that the dataset cache will
246            // have swallowed up all available file handles, in which case
247            // the server admin will need to increase the number of available
248            // handles on the server.
249            nc = NetcdfDataset.openDataset(location);
250        }
251        long openedDS = System.nanoTime();
252        String verb = usedCache ? "Acquired" : "Opened";
253        logger.debug(verb + " NetcdfDataset in {} milliseconds", (openedDS - start) / 1.e6);
254        return nc;
255    }
256
257    /** Closes the given dataset, logging any exceptions at debug level */
258    private static void closeDataset(NetcdfDataset nc)
259    {
260        if (nc == null) return;
261        try
262        {
263            nc.close();
264            logger.debug("NetCDF file closed");
265        }
266        catch (IOException ex)
267        {
268            logger.error("IOException closing " + nc.getLocation(), ex);
269        }
270    }
271   
272}
Note: See TracBrowser for help on using the repository browser.