From 1e0026e60f67cf5a6cc425187e137c5b7bc9a0b5 Mon Sep 17 00:00:00 2001 From: Nate Jensen Date: Thu, 27 Mar 2014 18:32:00 -0500 Subject: [PATCH] Issue #2015 fix NPP displaying in collaboration shared display sessions Change-Id: Ie5e40d8ff3e7a222448777b86d188822e3990746 Former-commit-id: d4e2da7f81d5c3ad005f299c936bd2d70f05b17a --- .../viirs/projection/VIIRSMapProjection.java | 19 ++ .../projection/VIIRSMapProjectionFactory.java | 16 +- .../adapters/GridGeometry2DAdapter.java | 183 +++++++++++++++++- 3 files changed, 207 insertions(+), 11 deletions(-) diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.npp.viirs/src/com/raytheon/uf/common/dataplugin/npp/viirs/projection/VIIRSMapProjection.java b/edexOsgi/com.raytheon.uf.common.dataplugin.npp.viirs/src/com/raytheon/uf/common/dataplugin/npp/viirs/projection/VIIRSMapProjection.java index 86365f2910..c2d7e57dca 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.npp.viirs/src/com/raytheon/uf/common/dataplugin/npp/viirs/projection/VIIRSMapProjection.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.npp.viirs/src/com/raytheon/uf/common/dataplugin/npp/viirs/projection/VIIRSMapProjection.java @@ -62,6 +62,7 @@ import com.vividsolutions.jts.geom.LineSegment; * algorithm to use great circle intersections for * more accurate results * Aug 27, 2013 #2190 mschenke Sped up transform functions + * Mar 27, 2014 #2015 njensen Overrode getParameterValues() * * * @@ -148,6 +149,24 @@ public class VIIRSMapProjection extends MapProjection { return Provider.PARAMETERS; } + @Override + public ParameterValueGroup getParameterValues() { + final ParameterDescriptorGroup descriptor = getParameterDescriptors(); + final ParameterValueGroup values = descriptor.createValue(); + values.parameter(CENTER_LATITUDES).setValue(centerLats); + values.parameter(CENTER_LONGITUDES).setValue(centerLons); + values.parameter(DIRECTIONS).setValue(directions); + values.parameter(CENTER_LENGTH).setValue(actualHeight); + values.parameter(RESOLUTION).setValue(resolution); + values.parameter(SEMI_MAJOR).setValue( + VIIRSMapProjectionFactory.SEMI_MINOR_MAJOR_VALUE); + values.parameter(SEMI_MINOR).setValue( + VIIRSMapProjectionFactory.SEMI_MINOR_MAJOR_VALUE); + values.parameter(Provider.CENTRAL_MERIDIAN.getName().getCode()) + .setValue(Provider.CENTRAL_MERIDIAN.getDefaultValue()); + return values; + } + /* * (non-Javadoc) * diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.npp.viirs/src/com/raytheon/uf/common/dataplugin/npp/viirs/projection/VIIRSMapProjectionFactory.java b/edexOsgi/com.raytheon.uf.common.dataplugin.npp.viirs/src/com/raytheon/uf/common/dataplugin/npp/viirs/projection/VIIRSMapProjectionFactory.java index 3b7c5234a6..edb1204a2a 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.npp.viirs/src/com/raytheon/uf/common/dataplugin/npp/viirs/projection/VIIRSMapProjectionFactory.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.npp.viirs/src/com/raytheon/uf/common/dataplugin/npp/viirs/projection/VIIRSMapProjectionFactory.java @@ -19,7 +19,6 @@ **/ package com.raytheon.uf.common.dataplugin.npp.viirs.projection; -import org.geotools.referencing.crs.DefaultProjectedCRS; import org.geotools.referencing.operation.DefaultMathTransformFactory; import org.opengis.parameter.ParameterValueGroup; import org.opengis.referencing.FactoryException; @@ -38,6 +37,7 @@ import com.raytheon.uf.common.geospatial.MapUtil; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Jul 16, 2012 mschenke Initial creation + * Mar 27, 2014 2015 njensen Made semi minor/major a constant * * * @@ -50,6 +50,12 @@ public class VIIRSMapProjectionFactory { /** Using single factory is faster due to internal caching */ private static DefaultMathTransformFactory dmtFactory = new DefaultMathTransformFactory(); + /** + * MapProjection requires semi_major/minor but our projection does not use + * them + */ + protected static final double SEMI_MINOR_MAJOR_VALUE = 1.0; + public static ProjectedCRS construct(VIIRSSpatialCoverage record) throws FactoryException { try { @@ -65,10 +71,10 @@ public class VIIRSMapProjectionFactory { record.getDirections()); group.parameter(VIIRSMapProjection.RESOLUTION).setValue( record.getDy().doubleValue()); - // MapProjection requires semi_major/minor but our projection does - // not use them - group.parameter(VIIRSMapProjection.SEMI_MAJOR).setValue(1.0); - group.parameter(VIIRSMapProjection.SEMI_MINOR).setValue(1.0); + group.parameter(VIIRSMapProjection.SEMI_MAJOR).setValue( + SEMI_MINOR_MAJOR_VALUE); + group.parameter(VIIRSMapProjection.SEMI_MINOR).setValue( + SEMI_MINOR_MAJOR_VALUE); return MapUtil.constructProjection( VIIRSMapProjection.PROJECTION_NAME, group); } catch (Exception e) { diff --git a/edexOsgi/com.raytheon.uf.common.serialization/src/com/raytheon/uf/common/serialization/adapters/GridGeometry2DAdapter.java b/edexOsgi/com.raytheon.uf.common.serialization/src/com/raytheon/uf/common/serialization/adapters/GridGeometry2DAdapter.java index eb1f59435b..db77a84fb6 100644 --- a/edexOsgi/com.raytheon.uf.common.serialization/src/com/raytheon/uf/common/serialization/adapters/GridGeometry2DAdapter.java +++ b/edexOsgi/com.raytheon.uf.common.serialization/src/com/raytheon/uf/common/serialization/adapters/GridGeometry2DAdapter.java @@ -19,12 +19,33 @@ **/ package com.raytheon.uf.common.serialization.adapters; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import org.geotools.coverage.grid.GridEnvelope2D; import org.geotools.coverage.grid.GridGeometry2D; import org.geotools.geometry.Envelope2D; +import org.geotools.parameter.DefaultParameterDescriptor; +import org.geotools.parameter.Parameter; +import org.geotools.parameter.ParameterGroup; import org.geotools.referencing.CRS; +import org.geotools.referencing.crs.DefaultProjectedCRS; +import org.geotools.referencing.cs.DefaultCartesianCS; +import org.geotools.referencing.operation.DefaultMathTransformFactory; +import org.geotools.referencing.operation.DefiningConversion; import org.opengis.geometry.Envelope; +import org.opengis.parameter.GeneralParameterValue; +import org.opengis.parameter.ParameterDescriptor; +import org.opengis.parameter.ParameterValue; +import org.opengis.parameter.ParameterValueGroup; import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.opengis.referencing.crs.GeographicCRS; +import org.opengis.referencing.crs.ProjectedCRS; +import org.opengis.referencing.cs.CartesianCS; +import org.opengis.referencing.operation.Conversion; +import org.opengis.referencing.operation.MathTransform; +import org.opengis.util.InternationalString; import com.raytheon.uf.common.serialization.IDeserializationContext; import com.raytheon.uf.common.serialization.ISerializationContext; @@ -32,14 +53,18 @@ import com.raytheon.uf.common.serialization.ISerializationTypeAdapter; import com.raytheon.uf.common.serialization.SerializationException; /** - * TODO Add Description + * Dynamic Serialize type adapter for GridGeometry2D * *
  * 
  * SOFTWARE HISTORY
  * Date         Ticket#    Engineer    Description
  * ------------ ---------- ----------- --------------------------
- * Jan 5, 2010            randerso     Initial creation
+ * Jan 5, 2010             randerso    Initial creation
+ * Mar 27, 2014 2015       njensen     Added ParameterValueAdapter and
+ *                                      serialize PROJCS differently to
+ *                                      work around geotools WKT limitations
+ * 
  * 
  * 
* @@ -50,6 +75,87 @@ import com.raytheon.uf.common.serialization.SerializationException; public class GridGeometry2DAdapter implements ISerializationTypeAdapter { + /** + * Serialization adapter for ParameterValues. Also has to serialize the + * descriptor, default, etc due to geotools API. Intentionally private class + * because it's barely better than a hack to work around geotools + * limitations of recreating objects from a serialized form. See + * http://jira.codehaus.org/browse/GEOT-4752 for more information. + */ + @SuppressWarnings(value = { "unchecked", "rawtypes" }) + private static class ParameterValueAdapter implements + ISerializationTypeAdapter { + + @Override + public void serialize(ISerializationContext serializer, + ParameterValue object) throws SerializationException { + // serialize everything for the descriptor first + ParameterDescriptor desc = object.getDescriptor(); + serializer.writeString(desc.getName().getCode()); + InternationalString rmk = desc.getRemarks(); + if (rmk != null) { + serializer.writeString(rmk.toString()); + } else { + serializer.writeString("null"); + } + serializer.writeString(desc.getValueClass().getName()); + serializer.writeObject(desc.getDefaultValue()); + serializer.writeBool(desc.getMinimumOccurs() > 0); + + // finally serialize the object itself + serializer.writeObject(object.getValue()); + } + + @Override + public ParameterValue deserialize( + IDeserializationContext deserializer) + throws SerializationException { + return deserializeInternal(deserializer); + } + + /** + * Internal deserialization, method exists entirely to work around Java + * generics issues with DefaultParameterDescriptor.create(...). + * + * @param deserializer + * @return + * @throws SerializationException + */ + private ParameterValue deserializeInternal( + IDeserializationContext deserializer) + throws SerializationException { + String nameCode = deserializer.readString(); + CharSequence rmk = deserializer.readString(); + if ("null".equals(rmk)) { + rmk = null; + } + + String classname = deserializer.readString(); + Class valueClass; + try { + valueClass = (Class) Class.forName(classname); + } catch (ClassNotFoundException e) { + throw new SerializationException( + "Unknown parameter value class " + classname); + } + + T defaultValue = (T) deserializer.readObject(); + boolean required = deserializer.readBool(); + Object value = deserializer.readObject(); + ParameterDescriptor desc = DefaultParameterDescriptor.create( + nameCode, rmk, valueClass, defaultValue, required); + Parameter param = new Parameter(desc, value); + return param; + + } + } + + private static final ParameterValueAdapter paramValAdapter = new ParameterValueAdapter(); + + private static final DefaultMathTransformFactory dmtFactory = new DefaultMathTransformFactory(); + + private static final String PROJCS = "PROJCS"; + @Override public GridGeometry2D deserialize(IDeserializationContext deserializer) throws SerializationException { @@ -61,8 +167,47 @@ public class GridGeometry2DAdapter implements int height = deserializer.readI32(); GridEnvelope2D gridRange = new GridEnvelope2D(x, y, width, height); - CoordinateReferenceSystem crs = CRS.parseWKT(deserializer - .readString()); + /* + * Some of our projected CRS instances don't conform to WKT due to + * custom parameters, so we have to handle those differently + */ + String crsType = deserializer.readString(); + CoordinateReferenceSystem crs = null; + if (PROJCS.equals(crsType)) { + Map props = null; + String projName = deserializer.readString(); + GeographicCRS baseCrs = (GeographicCRS) CRS + .parseWKT(deserializer.readString()); + String conversionName = deserializer.readString(); + + // recreate the ParameterValueGroup + String groupName = deserializer.readString(); + int paramValueSize = deserializer.readI32(); + GeneralParameterValue[] parameters = new GeneralParameterValue[paramValueSize]; + for (int i = 0; i < paramValueSize; i++) { + parameters[i] = paramValAdapter.deserialize(deserializer); + } + props = new HashMap(); + props.put("name", groupName); + ParameterGroup group = new ParameterGroup(props, parameters); + + // create the conversions with the group + DefiningConversion dc = new DefiningConversion(conversionName, + group); + MathTransform mt = dmtFactory + .createParameterizedTransform(group); + CartesianCS cs = DefaultCartesianCS.PROJECTED; + + props = new HashMap(); + props.put("name", projName); + crs = new DefaultProjectedCRS(props, dc, baseCrs, mt, cs); + } else { + /* + * only had special handling for ProjectedCRS, just hope it + * correctly parses anything else as WKT + */ + crs = CRS.parseWKT(deserializer.readString()); + } double dx = deserializer.readDouble(); double dy = deserializer.readDouble(); @@ -74,7 +219,7 @@ public class GridGeometry2DAdapter implements return gridGeom; } catch (Throwable e) { throw new SerializationException( - "Error deserializing GridGeomtry2D", e); + "Error deserializing GridGeometry2D", e); } } @@ -86,7 +231,33 @@ public class GridGeometry2DAdapter implements serializer.writeI32(gridGeom.getGridRange2D().width); serializer.writeI32(gridGeom.getGridRange2D().height); - serializer.writeString(gridGeom.getCoordinateReferenceSystem().toWKT()); + CoordinateReferenceSystem crs = gridGeom.getCoordinateReferenceSystem(); + if (crs instanceof ProjectedCRS) { + serializer.writeString(PROJCS); + ProjectedCRS projCrs = (ProjectedCRS) crs; + String projName = projCrs.getName().toString(); + serializer.writeString(projName); + serializer.writeString(projCrs.getBaseCRS().toWKT()); + + Conversion conversion = projCrs.getConversionFromBase(); + serializer.writeString(conversion.getName().toString()); + ParameterValueGroup params = conversion.getParameterValues(); + serializer.writeString(params.getDescriptor().getName().getCode()); + List values = params.values(); + serializer.writeI32(values.size()); + for (GeneralParameterValue v : values) { + paramValAdapter.serialize(serializer, (ParameterValue) v); + } + } else { + /* + * Cross fingers and hope it works ok with WKT. Seems to generally + * always write to WKT ok but then fails to parse it back out on + * some custom CRS instances. + */ + serializer.writeString("WKT"); + serializer.writeString(crs.toWKT()); + } + serializer.writeDouble(gridGeom.getEnvelope().getMinimum(0)); serializer.writeDouble(gridGeom.getEnvelope().getMinimum(1)); serializer.writeDouble(gridGeom.getEnvelope().getSpan(0));