Issue #704 create a radial bin and azimuth range map projection so radar data can use standard api.

Change-Id: If5bba07e679881188b190363cc234ba145ce9f23

Former-commit-id: dd353747d0 [formerly f99f9b49b0682fccf17f6d03d5cbde7f80cf7b39]
Former-commit-id: f63d2bc7c2
This commit is contained in:
Ben Steffensmeier 2012-06-14 16:28:55 -05:00
parent 6b9d13211d
commit e629fcb15b
5 changed files with 451 additions and 1 deletions

View file

@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2
Bundle-Name: Radar
Bundle-SymbolicName: com.raytheon.uf.common.dataplugin.radar
Bundle-Version: 1.0.0.qualifier
Eclipse-RegisterBuddy: com.raytheon.edex.common, com.raytheon.uf.common.serialization, com.raytheon.viz.core
Eclipse-RegisterBuddy: com.raytheon.edex.common, com.raytheon.uf.common.serialization, com.raytheon.viz.core, org.geotools
Bundle-Activator: com.raytheon.uf.common.dataplugin.radar.Activator
Bundle-Vendor: RAYTHEON
Require-Bundle: org.eclipse.core.runtime,
@ -31,6 +31,7 @@ Export-Package: com.raytheon.uf.common.dataplugin.radar;
com.raytheon.uf.common.time",
com.raytheon.uf.common.dataplugin.radar.level3;uses:="com.raytheon.uf.common.dataplugin.radar.level3.generic,com.raytheon.uf.common.serialization",
com.raytheon.uf.common.dataplugin.radar.level3.generic;uses:="com.raytheon.uf.common.serialization",
com.raytheon.uf.common.dataplugin.radar.projection,
com.raytheon.uf.common.dataplugin.radar.request;uses:="com.raytheon.uf.common.serialization.comm",
com.raytheon.uf.common.dataplugin.radar.util;
uses:="com.raytheon.uf.common.dataplugin.radar,

View file

@ -0,0 +1,2 @@
com.raytheon.uf.common.dataplugin.radar.projection.AzimuthRangeMapProjection$Provider
com.raytheon.uf.common.dataplugin.radar.projection.RadialBinMapProjection$Provider

View file

@ -0,0 +1,130 @@
/**
* 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.uf.common.dataplugin.radar.projection;
import java.awt.geom.Point2D;
import org.geotools.parameter.DefaultParameterDescriptorGroup;
import org.geotools.referencing.operation.projection.ObliqueStereographic;
import org.geotools.referencing.operation.projection.ProjectionException;
import org.opengis.parameter.InvalidParameterNameException;
import org.opengis.parameter.InvalidParameterValueException;
import org.opengis.parameter.ParameterDescriptor;
import org.opengis.parameter.ParameterDescriptorGroup;
import org.opengis.parameter.ParameterNotFoundException;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.operation.MathTransform;
/**
* Coordinates in the AzimuthRangeProjection represent the azimuth and range
* relative to the point defined by the intersection of the central meridian and
* the latitude of origin. The x coordinate is the azimuth angle in degrees and
* the the y coordinate is the distance from the point in meters.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jun 6, 2012 bsteffen Initial creation
*
* </pre>
*
* @author bsteffen
* @version 1.0
*/
public class AzimuthRangeMapProjection extends ObliqueStereographic {
private static final long serialVersionUID = 4469133725910219322L;
protected AzimuthRangeMapProjection(ParameterValueGroup values)
throws ParameterNotFoundException {
super(values);
this.globalScale = 1.0;
}
@Override
public ParameterDescriptorGroup getParameterDescriptors() {
return Provider.PARAMETERS;
}
@Override
protected Point2D inverseTransformNormalized(double azimuth, double range,
Point2D dest) throws ProjectionException {
range = range / (scaleFactor * semiMajor);
azimuth = Math.toRadians(azimuth);
double x = range * Math.sin(azimuth);
double y = range * Math.cos(azimuth);
return super.inverseTransformNormalized(x, y, dest);
}
protected double normalizeAngle(double rangeLow, double rangeHigh,
double angle) {
while (angle < rangeLow) {
angle += 360;
}
while (angle > rangeHigh) {
angle -= 360;
}
return angle;
}
@Override
protected Point2D transformNormalized(double lon, double lat, Point2D dest)
throws ProjectionException {
Point2D tmp = new Point2D.Double();
super.transformNormalized(lon, lat, tmp);
double azimuth = Math.toDegrees(Math.atan2(tmp.getX(), tmp.getY()));
double range = scaleFactor * semiMajor
* Math.hypot(tmp.getX(), tmp.getY());
if (dest != null) {
dest.setLocation(azimuth, range);
return dest;
}
return new Point2D.Double(azimuth, range);
}
public static class Provider extends AbstractProvider {
private static final long serialVersionUID = 6736153862347982008L;
static final ParameterDescriptorGroup PARAMETERS = new DefaultParameterDescriptorGroup(
"Azimuth_Range", new ParameterDescriptor[] { SEMI_MAJOR,
SEMI_MINOR, CENTRAL_MERIDIAN, LATITUDE_OF_ORIGIN,
SCALE_FACTOR, FALSE_EASTING, FALSE_NORTHING });
public Provider() {
super(PARAMETERS);
}
@Override
protected MathTransform createMathTransform(ParameterValueGroup values)
throws InvalidParameterNameException,
ParameterNotFoundException, InvalidParameterValueException,
FactoryException {
return new AzimuthRangeMapProjection(values);
}
}
}

View file

@ -0,0 +1,117 @@
/**
* 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.uf.common.dataplugin.radar.projection;
import org.geotools.coverage.grid.GridEnvelope2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.geometry.Envelope2D;
import org.geotools.referencing.operation.DefaultMathTransformFactory;
import org.geotools.referencing.operation.builder.GridToEnvelopeMapper;
import org.geotools.referencing.operation.projection.MapProjection.AbstractProvider;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.ProjectedCRS;
import com.raytheon.uf.common.geospatial.MapUtil;
import com.vividsolutions.jts.geom.Coordinate;
/**
* TODO Add Description
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jun 7, 2012 bsteffen Initial creation
*
* </pre>
*
* @author bsteffen
* @version 1.0
*/
public class RadarProjectionFactory {
private static DefaultMathTransformFactory dmtFactory = new DefaultMathTransformFactory();
public static ProjectedCRS constructAzRan(Coordinate centerLatLon)
throws FactoryException {
ParameterValueGroup group = dmtFactory
.getDefaultParameters("Azimuth_Range");
group.parameter(AbstractProvider.SEMI_MAJOR.getName().getCode())
.setValue(MapUtil.AWIPS_EARTH_RADIUS);
group.parameter(AbstractProvider.SEMI_MINOR.getName().getCode())
.setValue(MapUtil.AWIPS_EARTH_RADIUS);
group.parameter(AbstractProvider.CENTRAL_MERIDIAN.getName().getCode())
.setValue(centerLatLon.x);
group.parameter(AbstractProvider.LATITUDE_OF_ORIGIN.getName().getCode())
.setValue(centerLatLon.y);
return MapUtil.constructProjection("Azimuth Range", group);
}
public static ProjectedCRS constructRadialBin(Coordinate centerLatLon,
float[] angleData, double binWidth, double tiltAngle)
throws FactoryException {
ParameterValueGroup group = dmtFactory
.getDefaultParameters("Radial_Bin");
group.parameter(AbstractProvider.SEMI_MAJOR.getName().getCode())
.setValue(MapUtil.AWIPS_EARTH_RADIUS);
group.parameter(AbstractProvider.SEMI_MINOR.getName().getCode())
.setValue(MapUtil.AWIPS_EARTH_RADIUS);
group.parameter(AbstractProvider.CENTRAL_MERIDIAN.getName().getCode())
.setValue(centerLatLon.x);
group.parameter(AbstractProvider.LATITUDE_OF_ORIGIN.getName().getCode())
.setValue(centerLatLon.y);
group.parameter(
RadialBinMapProjection.Provider.ANGLE_DATA.getName().getCode())
.setValue(angleData);
group.parameter(
RadialBinMapProjection.Provider.BIN_LENGTH.getName().getCode())
.setValue(binWidth);
group.parameter(
RadialBinMapProjection.Provider.TILT_ANGLE.getName().getCode())
.setValue(tiltAngle);
return MapUtil.constructProjection("Radial Bin", group);
}
public static GridGeometry2D constructGridGeometry(Coordinate centerLatLon,
float[] angleData, double binWidth, double tiltAngle, int numBins,
boolean swapXY) throws FactoryException {
CoordinateReferenceSystem crs = constructRadialBin(centerLatLon,
angleData, binWidth, tiltAngle);
GridEnvelope2D gridRange = null;
if (swapXY) {
gridRange = new GridEnvelope2D(0, 0, numBins, angleData.length);
} else {
gridRange = new GridEnvelope2D(0, 0, angleData.length, numBins);
}
Envelope2D envelope = new Envelope2D(crs, 0, 0, angleData.length,
numBins);
GridToEnvelopeMapper mapper = new GridToEnvelopeMapper(gridRange,
envelope);
mapper.setSwapXY(swapXY);
mapper.setReverseAxis(new boolean[] { false, false });
return new GridGeometry2D(gridRange, mapper.createTransform(), crs);
}
}

View file

@ -0,0 +1,200 @@
/**
* 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.uf.common.dataplugin.radar.projection;
import java.awt.geom.Point2D;
import java.util.Arrays;
import org.geotools.parameter.DefaultParameterDescriptor;
import org.geotools.parameter.DefaultParameterDescriptorGroup;
import org.geotools.referencing.operation.MathTransformProvider;
import org.geotools.referencing.operation.projection.ProjectionException;
import org.opengis.parameter.InvalidParameterNameException;
import org.opengis.parameter.InvalidParameterValueException;
import org.opengis.parameter.ParameterDescriptor;
import org.opengis.parameter.ParameterDescriptorGroup;
import org.opengis.parameter.ParameterNotFoundException;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.operation.MathTransform;
/**
* Coordinates in the RadialBinMapProjection represent the radial and bin
* relative to the point defined by the intersection of the central meridian and
* the latitude of origin. The x coordinate is the radial index into the
* angleData, the y coordinate is the bin number, which is computed using the
* binWidth and the tilt angle.
*
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jun 6, 2012 bsteffen Initial creation
*
* </pre>
*
* @author bsteffen
* @version 1.0
*/
public class RadialBinMapProjection extends AzimuthRangeMapProjection {
private static final long serialVersionUID = 6392115431894559801L;
private float[] normalAngleData;
private double flatBinLength;
protected RadialBinMapProjection(ParameterValueGroup values)
throws ParameterNotFoundException {
super(values);
float[] angleData = Provider.getValue(Provider.ANGLE_DATA, values);
normalAngleData = new float[angleData.length];
normalAngleData[0] = angleData[0];
for (int i = 1; i < angleData.length; i += 1) {
if (angleData[i] <= angleData[0]) {
normalAngleData[i] = angleData[i] + 360;
} else {
normalAngleData[i] = angleData[i];
}
}
double tiltAngle = Provider.getValue(Provider.TILT_ANGLE, values);
double binLength = Provider.getValue(Provider.BIN_LENGTH, values);
this.flatBinLength = binLength * Math.cos(Math.toRadians(tiltAngle));
}
@Override
public ParameterDescriptorGroup getParameterDescriptors() {
return Provider.PARAMETERS;
}
@Override
protected Point2D inverseTransformNormalized(double radial, double bin,
Point2D dest) throws ProjectionException {
double ran = bin * flatBinLength;
while (radial < 0) {
radial += normalAngleData.length;
}
int prevRadial = (int) Math.floor(radial) % normalAngleData.length;
int nextRadial = (int) Math.ceil(radial) % normalAngleData.length;
float prevAngle = normalAngleData[prevRadial];
float nextAngle = normalAngleData[nextRadial];
double az = prevAngle + (radial - prevRadial) * (nextAngle - prevAngle);
return super.inverseTransformNormalized(az, ran, dest);
}
@Override
protected Point2D transformNormalized(double lon, double lat, Point2D dest)
throws ProjectionException {
Point2D tmp = new Point2D.Double();
tmp = super.transformNormalized(lon, lat, tmp);
double az = tmp.getX();
double ran = tmp.getY();
// System.out.println(ran);
double radial = Double.NaN;
double bin = ran / flatBinLength;
float nextAngle = normalAngleData[0] + 360;
az = normalizeAngle(normalAngleData[0], nextAngle, az);
for (int i = normalAngleData.length - 1; i >= 0; i -= 1) {
float prevAngle = normalAngleData[i];
if (prevAngle <= az && nextAngle > az) {
radial = (i) + (az - prevAngle) / (nextAngle - prevAngle);
break;
}
prevAngle = nextAngle;
}
dest.setLocation(radial, bin);
return dest;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
long temp;
temp = Double.doubleToLongBits(flatBinLength);
result = prime * result + (int) (temp ^ (temp >>> 32));
result = prime * result + Arrays.hashCode(normalAngleData);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
RadialBinMapProjection other = (RadialBinMapProjection) obj;
if (Double.doubleToLongBits(flatBinLength) != Double
.doubleToLongBits(other.flatBinLength))
return false;
if (!Arrays.equals(normalAngleData, other.normalAngleData))
return false;
return true;
}
public static class Provider extends AbstractProvider {
private static final long serialVersionUID = 4990986103949071193L;
static final ParameterDescriptor<float[]> ANGLE_DATA = DefaultParameterDescriptor
.create("angle_data", "The angle in degrees of each radial",
float[].class, null, true);
static final ParameterDescriptor<Double> TILT_ANGLE = DefaultParameterDescriptor
.create("tilt_angle",
"The angle in degrees above ground level",
Double.class, null, true);
static final ParameterDescriptor<Double> BIN_LENGTH = DefaultParameterDescriptor
.create("bin_length", "The length of bins in meters",
Double.class, null, true);
static final ParameterDescriptorGroup PARAMETERS = new DefaultParameterDescriptorGroup(
"Radial_Bin", new ParameterDescriptor[] { SEMI_MAJOR,
SEMI_MINOR, CENTRAL_MERIDIAN, LATITUDE_OF_ORIGIN,
ANGLE_DATA, TILT_ANGLE, BIN_LENGTH, SCALE_FACTOR,
FALSE_EASTING, FALSE_NORTHING });
public Provider() {
super(PARAMETERS);
}
@Override
protected MathTransform createMathTransform(ParameterValueGroup values)
throws InvalidParameterNameException,
ParameterNotFoundException, InvalidParameterValueException,
FactoryException {
return new RadialBinMapProjection(values);
}
static <T> T getValue(ParameterDescriptor<T> descriptor,
ParameterValueGroup group) {
return MathTransformProvider.value(descriptor, group);
}
}
}