Issue #2502 Add data access factory for model sounding.

Former-commit-id: 65758112879a9007adb5c83721e47217b5eb1cd6
This commit is contained in:
Ben Steffensmeier 2013-11-04 11:42:26 -06:00
parent 3c862170db
commit e02ace2809
3 changed files with 521 additions and 2 deletions

View file

@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Modelsounding Plug-in
Bundle-SymbolicName: com.raytheon.edex.plugin.modelsounding
Bundle-Version: 1.12.1174.qualifier
Bundle-Version: 1.13.0.qualifier
Eclipse-RegisterBuddy: com.raytheon.edex.common, com.raytheon.uf.common.serialization
Bundle-Vendor: RAYTHEON
Require-Bundle: com.raytheon.edex.common,
@ -14,7 +14,13 @@ Require-Bundle: com.raytheon.edex.common,
com.raytheon.uf.common.site;bundle-version="1.12.1174",
com.raytheon.uf.common.status;bundle-version="1.12.1174",
org.apache.commons.lang;bundle-version="2.3.0",
com.google.guava;bundle-version="1.0.0"
com.google.guava;bundle-version="1.0.0",
javax.measure,
com.raytheon.uf.common.comm,
com.raytheon.uf.common.dataaccess,
com.raytheon.uf.common.dataplugin.level,
com.raytheon.uf.common.dataquery,
com.raytheon.uf.common.serialization.comm
Export-Package: com.raytheon.edex.plugin.modelsounding,
com.raytheon.edex.plugin.modelsounding.common,
com.raytheon.edex.plugin.modelsounding.dao,

View file

@ -0,0 +1,29 @@
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<bean id="mdlsndDataAccessFactory" class="com.raytheon.edex.plugin.modelsounding.dataaccess.PointDataAccessFactory" />
<bean factory-bean="dataAccessRegistry" factory-method="register">
<constructor-arg value="modelsounding"/>
<constructor-arg ref="mdlsndDataAccessFactory"/>
</bean>
<bean factory-bean="mdlsndDataAccessFactory" factory-method="register2D">
<constructor-arg value="numProfLvls"/>
<constructor-arg value="pressure"/>
<constructor-arg value="MB"/>
<constructor-arg>
<list>
<value>pressure</value>
<value>temperature</value>
<value>specHum</value>
<value>omega</value>
<value>uComp</value>
<value>vComp</value>
<value>cldCvr</value>
</list>
</constructor-arg>
</bean>
</beans>

View file

@ -0,0 +1,484 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.edex.plugin.modelsounding.dataaccess;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.measure.unit.Unit;
import javax.measure.unit.UnitFormat;
import com.raytheon.uf.common.comm.CommunicationException;
import com.raytheon.uf.common.dataaccess.DataAccessLayer;
import com.raytheon.uf.common.dataaccess.IDataRequest;
import com.raytheon.uf.common.dataaccess.exception.DataRetrievalException;
import com.raytheon.uf.common.dataaccess.exception.UnsupportedOutputTypeException;
import com.raytheon.uf.common.dataaccess.geom.IGeometryData;
import com.raytheon.uf.common.dataaccess.geom.IGeometryData.Type;
import com.raytheon.uf.common.dataaccess.grid.IGridData;
import com.raytheon.uf.common.dataaccess.impl.AbstractDataPluginFactory;
import com.raytheon.uf.common.dataaccess.impl.DefaultGeometryData;
import com.raytheon.uf.common.dataplugin.level.LevelFactory;
import com.raytheon.uf.common.dataplugin.level.MasterLevel;
import com.raytheon.uf.common.dataquery.requests.DbQueryRequest;
import com.raytheon.uf.common.dataquery.requests.RequestConstraint;
import com.raytheon.uf.common.dataquery.requests.RequestConstraint.ConstraintType;
import com.raytheon.uf.common.dataquery.responses.DbQueryResponse;
import com.raytheon.uf.common.pointdata.PointDataConstants;
import com.raytheon.uf.common.pointdata.PointDataContainer;
import com.raytheon.uf.common.pointdata.PointDataDescription;
import com.raytheon.uf.common.pointdata.PointDataServerRequest;
import com.raytheon.uf.common.pointdata.PointDataView;
import com.raytheon.uf.common.serialization.comm.RequestRouter;
import com.raytheon.uf.common.time.DataTime;
import com.raytheon.uf.common.time.TimeRange;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;
/**
* Data Access Factory for retrieving point data as a geometry.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------- -------- ----------- --------------------------
* Oct 31, 2013 2502 bsteffen Initial creation
*
* </pre>
*
* @author bsteffen
* @version 1.0
*/
public class PointDataAccessFactory extends AbstractDataPluginFactory {
// TODO this should be in PointDataServerRequest
private static final String REQUEST_PARAMETERS_KEY = "requestedParameters";
// TODO this should be in PointDataServerRequest
private static final String REQUEST_MODE_KEY = "mode";
// TODO this should be in PointDataServerRequest
private static final String REQUEST_MODE_2D = "select2d";
private static class TwoDimensionalParameterGroup {
public final String countParameter;
public final String levelParameter;
public final String levelType;
public final String[] parameters;
public TwoDimensionalParameterGroup(String countParameter,
String levelParameter, String levelType, String[] parameters) {
super();
this.countParameter = countParameter;
this.levelParameter = levelParameter;
this.levelType = levelType;
this.parameters = parameters;
}
}
private String locationDatabaseKey = "location.stationId";
private String locationPointDataKey = PointDataConstants.DATASET_STATIONID;
private String latitudePointDataKey = "latitude";
private String longitudePointDataKey = "longitude";
private String refTimePointDataKey = PointDataConstants.DATASET_REFTIME;
private String fcstHrPointDataKey = PointDataConstants.DATASET_FORECASTHR;
private Map<String, TwoDimensionalParameterGroup> parameters2D = new HashMap<String, TwoDimensionalParameterGroup>();
@Override
public String[] getAvailableLocationNames(IDataRequest request) {
return getAvailableLocationNames(request, locationDatabaseKey);
}
@Override
public IGeometryData[] getGeometryData(IDataRequest request,
DataTime... times) {
/*
* Point data uses PointDataServerRequest instead of the DbQueryRequest
* that is used in AbstractDataPluginFactory. Override this method so
* the DbQueryRequest can be converted to a PointDataServerRequest
*/
validateRequest(request);
DbQueryRequest dbQueryRequest = this
.buildDbQueryRequest(request, times);
return getGeometryData(request, dbQueryRequest);
}
@Override
public IGeometryData[] getGeometryData(IDataRequest request,
TimeRange timeRange) {
/*
* Point data uses PointDataServerRequest instead of the DbQueryRequest
* that is used in AbstractDataPluginFactory. Override this method so
* the DbQueryRequest can be converted to a PointDataServerRequest
*/
validateRequest(request);
DbQueryRequest dbQueryRequest = this.buildDbQueryRequest(request,
timeRange);
return getGeometryData(request, dbQueryRequest);
}
@Override
protected IGeometryData[] getGeometryData(IDataRequest request,
DbQueryResponse dbQueryResponse) {
/*
* Since the public getGeometryData methods have been overriden, this is
* now unreachable code, but since it is an abstract method in the super
* class it must be implemented.
*/
throw new UnsupportedOperationException(
"This method should be unreachable");
}
@Override
protected IGridData[] getGridData(IDataRequest request,
DbQueryResponse dbQueryResponse) {
/*
* Point data cannot be gridded, so don't even try.
*/
throw new UnsupportedOutputTypeException(request.getDatatype(), "grid");
}
@Override
protected Map<String, RequestConstraint> buildConstraintsFromRequest(
IDataRequest request) {
Map<String, RequestConstraint> rcMap = new HashMap<String, RequestConstraint>();
String[] locations = request.getLocationNames();
if (locations != null && locations.length != 0) {
RequestConstraint rc = new RequestConstraint();
rc.setConstraintType(ConstraintType.IN);
rc.setConstraintValueList(locations);
rcMap.put(locationDatabaseKey, rc);
}
Map<String, Object> identifiers = request.getIdentifiers();
if (identifiers != null) {
for (Entry<String, Object> entry : identifiers.entrySet()) {
rcMap.put(entry.getKey(), new RequestConstraint(entry
.getValue().toString()));
}
}
return rcMap;
}
/**
*
* Request point data from the server and convert to {@link IGeometryData}
*
* @param request
* the original request from the {@link DataAccessLayer}
* @param dbQueryRequest
* the request generated by {@link AbstractDataPluginFactory},
* this will be converted into a {@link PointDataServerRequest}.
* @return {@link IGeometryData}
*/
protected IGeometryData[] getGeometryData(IDataRequest request,
DbQueryRequest dbQueryRequest) {
PointDataServerRequest serverRequest = convertRequest(request,
dbQueryRequest);
PointDataContainer pdc = null;
try {
pdc = (PointDataContainer) RequestRouter.route(serverRequest);
} catch (Exception e) {
throw new DataRetrievalException(
"Unable to complete the PointDataRequestMessage for request: "
+ request, e);
}
LevelFactory lf = LevelFactory.getInstance();
/* Convert the point data container into a list of IGeometryData */
List<IGeometryData> result = new ArrayList<IGeometryData>(
pdc.getAllocatedSz());
for (int i = 0; i < pdc.getCurrentSz(); i += 1) {
PointDataView pdv = pdc.readRandom(i);
DefaultGeometryData data = createNewGeometryData(pdv);
try {
data.setLevel(lf.getLevel(LevelFactory.UNKNOWN_LEVEL, 0.0));
} catch (CommunicationException e) {
throw new DataRetrievalException(
"Unable to retrieve level data for request: " + request,
e);
}
Set<TwoDimensionalParameterGroup> parameters2D = new HashSet<TwoDimensionalParameterGroup>();
for (String parameter : request.getParameters()) {
if (pdc.getParameters().contains(parameter)) {
int dim = pdc.getDimensions(parameter);
if (dim == 1) {
Unit<?> unit = pdv.getUnit(parameter);
PointDataDescription.Type type = pdv.getType(parameter);
if (type == PointDataDescription.Type.STRING) {
data.addData(parameter, pdv.getString(parameter),
Type.STRING, unit);
} else {
data.addData(parameter, pdv.getNumber(parameter),
unit);
}
} else if (this.parameters2D.containsKey(parameter)) {
parameters2D.add(this.parameters2D.get(parameter));
} else {
throw new DataRetrievalException(
"PointDataAccessFactory cannot handle " + dim
+ "D parameters: " + parameter);
}
}
}
for (TwoDimensionalParameterGroup p2d : parameters2D) {
result.addAll(make2DData(request, p2d, pdv));
}
if (!data.getParameters().isEmpty()) {
result.add(data);
}
}
return result.toArray(new IGeometryData[0]);
}
/**
* Pull the constraints ouf of a {@link DbQueryRequest} and combine the
* information with an {@link IDataRequest} to build a
* {@link PointDataServerRequest}. This is done because
* {@link AbstractDataPluginFactory} makes really nice DbQueryRequests but
* we can't use them for point data.
*
* @param request
* @param dbQueryRequest
* @return
*/
private PointDataServerRequest convertRequest(IDataRequest request,
DbQueryRequest dbQueryRequest) {
Map<String, RequestConstraint> constraints = dbQueryRequest
.getConstraints();
constraints.put(REQUEST_MODE_KEY,
new RequestConstraint(REQUEST_MODE_2D));
/*
* Figure out what parameters we actually need.
*/
Set<String> parameters = new HashSet<String>();
Set<TwoDimensionalParameterGroup> parameters2D = new HashSet<TwoDimensionalParameterGroup>();
for (String parameter : request.getParameters()) {
/*
* Make sure that any 2D parameters also have the count parameter
* requested.
*/
TwoDimensionalParameterGroup p2d = this.parameters2D.get(parameter);
if (p2d != null) {
parameters.add(p2d.countParameter);
parameters.add(p2d.levelParameter);
parameters2D.add(p2d);
}
parameters.add(parameter);
}
/* Always request location parameters */
parameters.add(locationPointDataKey);
parameters.add(latitudePointDataKey);
parameters.add(longitudePointDataKey);
parameters.add(refTimePointDataKey);
if (fcstHrPointDataKey != null) {
parameters.add(fcstHrPointDataKey);
}
RequestConstraint rc = new RequestConstraint();
rc.setConstraintType(ConstraintType.IN);
rc.setConstraintValueList(parameters.toArray(new String[0]));
constraints.put(REQUEST_PARAMETERS_KEY, rc);
return new PointDataServerRequest(constraints);
}
/**
* Pull out location and time data from a {@link PointDataView} to build a
* {@link DefaultGeometryData}.
*
* @param pdv
* view for a single record
* @return {@link DefaultGeometryData} with locationName, time, and geometry
* set.
*/
private DefaultGeometryData createNewGeometryData(PointDataView pdv) {
DefaultGeometryData data = new DefaultGeometryData();
data.setLocationName(pdv.getString(locationPointDataKey));
long refTime = pdv.getNumber(refTimePointDataKey).longValue();
if (fcstHrPointDataKey != null) {
int fcstTime = pdv.getNumber(fcstHrPointDataKey).intValue();
data.setDataTime(new DataTime(new Date(refTime), fcstTime));
} else {
data.setDataTime(new DataTime(new Date(refTime)));
}
Coordinate c = new Coordinate(pdv.getFloat(longitudePointDataKey),
pdv.getFloat(latitudePointDataKey));
data.setGeometry(new GeometryFactory().createPoint(c));
// TODO python will break if attributes is null
data.setAttributes(new HashMap<String, Object>(0));
return data;
}
/**
* Make a {@link IGeometryData} object for each level in a 2 dimensional
* data set.
*
* @param request
* the original request
* @param p2d
* The 2d Parameter group
* @param pdv
* pdv contining data.
* @return One IGeometryData for each valid level in the 2d group.
*/
private List<IGeometryData> make2DData(IDataRequest request,
TwoDimensionalParameterGroup p2d, PointDataView pdv) {
List<String> requestParameters = Arrays.asList(request.getParameters());
LevelFactory lf = LevelFactory.getInstance();
int count = pdv.getInt(p2d.countParameter);
List<IGeometryData> result = new ArrayList<IGeometryData>(count);
for (int j = 0; j < count; j += 1) {
/* Clone the data, not level or parameters though */
DefaultGeometryData leveldata = createNewGeometryData(pdv);
double levelValue = pdv.getNumberAllLevels(p2d.levelParameter)[j]
.doubleValue();
String levelUnit = UnitFormat.getUCUMInstance().format(
pdv.getUnit(p2d.levelParameter));
try {
leveldata.setLevel(lf.getLevel(p2d.levelType, levelValue,
levelUnit));
} catch (CommunicationException e) {
throw new DataRetrievalException(
"Unable to retrieve level data for request: " + request,
e);
}
for (String parameter : p2d.parameters) {
if (requestParameters.contains(parameter)) {
Unit<?> unit = pdv.getUnit(parameter);
PointDataDescription.Type type = pdv.getType(parameter);
if (type == PointDataDescription.Type.STRING) {
leveldata.addData(parameter,
pdv.getStringAllLevels(parameter)[j],
Type.STRING, unit);
} else {
leveldata.addData(parameter,
pdv.getNumberAllLevels(parameter)[j], unit);
}
}
}
result.add(leveldata);
}
return result;
}
/**
* Point data types with 2 dimensions need to register so the 2d parameters
* can be grouped appropriately
*
* @param countParameter
* parameter name of an integer parameter identifying the number
* of valid levels.
* @param levelParameter
* parameter which should be used to build the level object in
* IGeometryData, for example "pressure"
* @param levelType
* {@link MasterLevel} name for the levelParameter, for example
* "MB"
* @param parameters
* all the parameters that are valid on the same 2D levels.
* @return countParameter is returned so spring can have a bean.
*/
public String register2D(String countParameter, String levelParameter,
String levelType, String[] parameters) {
TwoDimensionalParameterGroup td = new TwoDimensionalParameterGroup(
countParameter, levelParameter, levelType, parameters);
for (String parameter : parameters) {
parameters2D.put(parameter, td);
}
return countParameter;
}
/**
* @param locationDatabaseKey
* The hibernate field name of the field that is used to identify
* location names. Default values is "location.stationId"
*/
public void setLocationDatabaseKey(String locationDatabaseKey) {
this.locationDatabaseKey = locationDatabaseKey;
}
/**
* @param locationPointDataKey
* The point data key that matches the location database key.
* Defaults to "stationId"
*/
public void setLocationPointDataKey(String locationPointDataKey) {
this.locationPointDataKey = locationPointDataKey;
}
/**
* @param latitudePointDataKey
* The point data key of the station latitude. Default value is
* "latitude"
*/
public void setLatitudePointDataKey(String latitudePointDataKey) {
this.latitudePointDataKey = latitudePointDataKey;
}
/**
* @param longitudePointDataKey
* The point data key of the station longitude. Default value is
* "longitude"
*/
public void setLongitudePointDataKey(String longitudePointDataKey) {
this.longitudePointDataKey = longitudePointDataKey;
}
/**
* @param refTimePointDataKey
* The point data key of the reference time. Default value is
* "refTime"
*/
public void setRefTimePointDataKey(String refTimePointDataKey) {
this.refTimePointDataKey = refTimePointDataKey;
}
/**
* @param fcstHrPointDataKey
* The point data key of the forecast hour. Default value is
* "forecastHr". For live data with no forecast times this can be
* set to null so that it is not retrieved.
*/
public void setFcstHrPointDataKey(String fcstHrPointDataKey) {
this.fcstHrPointDataKey = fcstHrPointDataKey;
}
}