// // AddePointDataReader.java // /* This source file is part of the edu.wisc.ssec.mcidas package and is Copyright (C) 1998 - 2017 by Tom Whittaker, Tommy Jasmin, Tom Rink, Don Murray, James Kelly, Bill Hibbard, Dave Glowacki, Curtis Rueden and others. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ package edu.wisc.ssec.mcidas.adde; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.util.Vector; import edu.wisc.ssec.mcidas.McIDASUtil; /** * AddePointDataReader interface for McIDAS ADDE point data sets. Simulates a * McIDAS PTLIST output using an ADDE URL if <code>toString() method is used. * * Note that units are ignored by this client, default units are used. * * <pre> * URLs must all have the following format * adde://host/point?keyword_1=value_1&keyword_2=value_2 * * there can be any valid combination of the following supported keywords: * * group=<groupname> ADDE group name * descr=<descriptor> ADDE descriptor name * pos=<position> request an absolute or relative ADDE * position number * select=<select clause> to specify which data is required * param=<param list> what parameters to return * num=<max> maximum number of obs to return * user=<user_id> ADDE user identification * proj=<proj #> a valid ADDE project number * trace=<0/1> setting to 1 tells server to write debug * trace file (imagedata, imagedirectory) * version=1 ADDE version number, currently 1 * * the following keywords are required: * * group * descr * * an example URL might look like: * adde://rtds/point?group=neons&type=metar * </pre> * * @author Don Murray - Unidata and James Kelly - BoM * */ public class AddePointDataReader { // load protocol for ADDE URLs // See java.net.URL for explanation of URL handling static { try { String handlers = System.getProperty("java.protocol.handler.pkgs"); String newProperty = null; if (handlers == null) newProperty = "edu.wisc.ssec.mcidas"; else if (handlers.indexOf("edu.wisc.ssec.mcidas") < 0) newProperty = "edu.wisc.ssec.mcidas | " + handlers; if (newProperty != null) // was set above System.setProperty("java.protocol.handler.pkgs", newProperty); } catch (Exception e) { System.out.println( "Unable to set System Property: java.protocol.handler.pkgs"); } } /** * Key for getting data in param order [numParams][numObs] */ public static final int PARAM_ORDER = 0; /** * Key for getting data in obs order [numObs][numParams] */ public static final int OB_ORDER = 1; /** * Maximum number of parameters - used as a sanity check */ public static final int MAXNUMPARM = 402; // number of parameters private int status=0; // read status //private URLConnection urlc; // URL connection private String[] params; // parameters returned from server private int[] ScalingFactors; // scaling factors returned from server private String[] units; // units returned from server private int[][] iData = null; // data returned from server as array of ints private int[][] oData = null; // data returned from server as array of ints private Vector data = null; // holds the obs private int numParams = 0; // number of parameters private boolean debug = false; // set to true for debugging /** * creates an AddePointDataReader object that allows reading ADDE point * datasets. * * @param request ADDE URL to read from. See class javadoc. * * <pre> * an example URL might look like: * adde://rtds.ho.bom.gov.au/point?group=neons&descr=metar * </pre> * * @exception AddeException if there are no datasets of the particular * type or there is an error reading data * */ public AddePointDataReader(String request) throws AddeException { DataInputStream dataInputStream; URLConnection urlc; try { URL url = new URL(request); urlc = url.openConnection(); //InputStream is = urlc.getInputStream(); dataInputStream = new DataInputStream( new BufferedInputStream( urlc.getInputStream())); } catch (AddeURLException ae) { throw new AddeException("No datasets found", ae); } catch (Exception e) { throw new AddeException("Error opening connection", e); } // // first get number of bytes for Parameter Names // int numParamBytes; if(urlc instanceof AddeURLConnection) { numParamBytes = ((AddeURLConnection) urlc).getInitialRecordSize(); } else { try { numParamBytes = dataInputStream.readInt(); } catch (IOException e) { throw new AddeException("Error reading data", e); } } if (debug) System.out.println("numParamBytes = " + numParamBytes); if (numParamBytes == 0) { status = -1; throw new AddeException("No data found"); } else if (numParamBytes/4 > MAXNUMPARM) { status = -1; throw new AddeException("Not an ADDE Point Data set"); } else { byte[] bParamNames = new byte[numParamBytes]; numParams = numParamBytes/4; params = new String[numParams]; try { // // read Parameter names into paramNames // dataInputStream.readFully(bParamNames, 0, numParamBytes); String sParamNames = new String(bParamNames); if (debug) System.out.println(" sParamNames = " + sParamNames); for (int i = 0; i < numParams; i++) params[i] = sParamNames.substring(i*4, (i+1)*4).trim(); } catch (IOException e) { status = -1; throw new AddeException("Error reading parameters", e); } } // // next get number of bytes for Unit Names // try { int numUnitBytes = dataInputStream.readInt(); units = new String[numUnitBytes/4]; if (debug) System.out.println("numUnitBytes = " + numUnitBytes); byte[] bUnitNames = new byte[numUnitBytes]; dataInputStream.readFully(bUnitNames, 0, numUnitBytes); String sUnitNames = new String(bUnitNames); if (debug) System.out.println("sUnitNames = " + sUnitNames); for (int i = 0; i < numUnitBytes/4; i++) units[i] = sUnitNames.substring(i*4, (i+1)*4).trim(); } catch (IOException e) { status = -1; throw new AddeException("Error reading units", e); } // // next get number of bytes for Scaling Factors // try { int numScalingBytes = dataInputStream.readInt(); if (debug) System.out.println("numScalingBytes = " + numScalingBytes); ScalingFactors = new int[(int)(numScalingBytes/4)]; for (int i=0; i < (int) (numScalingBytes/4); i++) { ScalingFactors[i] = dataInputStream.readInt(); } } catch (IOException e) { status = -1; throw new AddeException("Error reading scaling factors", e); } // // next get number of bytes for the actual data // data = new Vector(); byte[] bThisUnitName = new byte[4]; try { int numDataBytes = dataInputStream.readInt(); while (numDataBytes !=0) { if (debug) System.out.println(" i, Param, Unit, Value " ); int[] dataArray = new int[numParams]; for (int i=0; i < (int) (numDataBytes/4); i++) { dataArray[i] = dataInputStream.readInt(); } data.addElement(dataArray); numDataBytes = dataInputStream.readInt(); if (debug) System.out.println("numDataBytes = " + numDataBytes); } } catch (IOException e) { status = -1; throw new AddeException("Error reading data", e); } } /** * Return the data sent by the server * * @return array of the data. Data is in the format of an integer array * of unscaled integers as returned from the server in parameter * order ([numParams][numObs]). * * @exception AddeException if there was an error reading data */ public int[][] getData() throws AddeException { return getData(PARAM_ORDER); } /** * Return the data sent by the server in a particular order * (PARAM_ORDER, OB_ORDER). * @param order order of the data. (PARAM_ORDER, OB_ORDER) * * @return array of the data. Data is in the format of an integer array * of unscaled integers in the specified order * (PARAM_ORDER = [numParams][numObs], * OB_ORDER = [numObs][numParams]) * * @exception AddeException if there was an error reading data */ public int[][] getData(int order) throws AddeException { if (status < 0) throw new AddeException("No data available"); return (order == PARAM_ORDER) ? getParamOrderData() : getObOrderData(); } /** * Get the list of parameters * * @return array of the parameter names. The names will be in the same * order as the array of data values in the <code>getData()</code> * method. * * @exception AddeException if there was an error reading data */ public String[] getParams() throws AddeException { if (status < 0) throw new AddeException("No data available"); return params; } /** * Get the list of units * * @return array of the unit names. The names will be in the same * order as the array of data values in the <code>getData()</code> * method. * * @exception AddeException if there was an error reading data */ public String[] getUnits() throws AddeException { if (status < 0) throw new AddeException("No data available"); return units; } /** * Get the list of scaling factors * * @return array of the scaling factors (powers of 10). The scaling * factors will be in the same order as the array of data * values in the <code>getData()</code> method. * * @exception AddeException if there was an error reading data */ public int[] getScales() throws AddeException { if (status < 0) throw new AddeException("No data available"); return ScalingFactors; } /** * return the number of parameters * * @return number of parameters returned from the server */ public int getNumParams() throws AddeException { if (status < 0) throw new AddeException("No data available"); return numParams; } /** * Return an array of data for the particular parameter. * * @return array of values for the particular parameter or * null if an invalid parameter was entered. This * will return String[] for parameters with units of CHAR, * float[1][] for an array of non scaled data, and double[1][] for */ public Object[] getData(String parameter) throws AddeException { if (status < 0) throw new AddeException("No data available"); for (int i = 0; i < numParams; i++) { if (parameter.equalsIgnoreCase(params[i])) { iData = getParamOrderData(); if (units[i].equalsIgnoreCase("CHAR")) { String[] vals = new String[iData[i].length]; for (int j = 0; j < iData[i].length; j++) { vals[j] = McIDASUtil.intBitsToString(iData[i][j]); } return vals; } else if (ScalingFactors[i] != 0) { double[][] vals = new double[1][iData[i].length]; for (int j = 0; j < iData[i].length; j++) { vals[0][j] = iData[i][j]/Math.pow(10.0, (double) ScalingFactors[i]); } return vals; } else { float[][] vals = new float[1][iData[i].length]; for (int j = 0; j < iData[i].length; j++) { vals[0][j] = (float) iData[i][j]; } return vals; } } } return null; } /** * Create an array of data in parameter order [numParams][numobs]. * @return array of data in parameter order */ private int[][] getParamOrderData() { if (iData == null) { // Convert to in array iData = new int[numParams][data.size()]; if (debug) { System.out.println("number of data records = " + data.size()); } for (int i = 0; i < data.size(); i++) { int[] values = (int[]) data.get(i); for (int j = 0; j < numParams; j++) iData[j][i] = values[j]; } } return iData; } /** * Create an array of data in observation order [numObs][numParams]. * @return array of data in obs order */ private int[][] getObOrderData() { if (oData == null) { // Convert to in array oData = new int[data.size()][numParams]; if (debug) { System.out.println("number of data records = " + data.size()); } for (int i = 0; i < data.size(); i++) { oData[i] = (int[]) data.get(i); } } return oData; } /** * Return a formated string of the returned data * * @return formatted representation of the data ala McIDAS PTLIST command. */ public String toString() { if (status < 0) return new String("No data Available"); StringBuffer buf = new StringBuffer(); for (int i = 0; i < numParams; i++) { buf.append(params[i]); buf.append("["); buf.append(units[i]); buf.append("] "); buf.append("\t"); } buf.append("\n"); iData = getParamOrderData(); for (int i = 0; i < iData[0].length; i++) { for (int j = 0; j < numParams; j++) { if (units[j].equalsIgnoreCase("CHAR")) { buf.append(McIDASUtil.intBitsToString(iData[j][i])); } else if (ScalingFactors[j] != 0) { buf.append( iData[j][i] == McIDASUtil.MCMISSING ? " " : Double.toString( iData[j][i]/Math.pow(10.0, (double) ScalingFactors[j] ))); } else { buf.append( iData[j][i] == McIDASUtil.MCMISSING ? " " : Integer.toString(iData[j][i])); } buf.append("\t"); } buf.append("\n"); } return buf.toString(); } /** test by running 'java edu.wisc.ssec.mcidas.adde.AddePointDataReader' */ public static void main (String[] args) throws Exception { System.out.println("\nData Requested:"); String request = (args.length == 0) // ? "adde://servb.ho.bom.gov.au/point?group=neons&descr=metar&num=2&select='id ymml; time 22 24; day 1999317'&parm=id dir spd t[c] td[c] psl&pos=ALL&version=1" // Unidata server ? "adde://adde.ucar.edu/point?group=rtptsrc&descr=sfchourly&num=2&select='id ypph'&parm=id dir spd t[c] td[c] psl&pos=0&version=1&trace=1" : args[0]; AddePointDataReader ptlist = new AddePointDataReader(request); System.out.println(ptlist.toString()); } }