diff --git a/ncep/gov.noaa.nws.ncep.ui.pgen/localization/ncep/pgen/settings_tbl.xml b/ncep/gov.noaa.nws.ncep.ui.pgen/localization/ncep/pgen/settings_tbl.xml
index c0997426b5..79f271435c 100644
--- a/ncep/gov.noaa.nws.ncep.ui.pgen/localization/ncep/pgen/settings_tbl.xml
+++ b/ncep/gov.noaa.nws.ncep.ui.pgen/localization/ncep/pgen/settings_tbl.xml
@@ -35,7 +35,7 @@
FL300
-
+
diff --git a/ncep/gov.noaa.nws.ncep.ui.pgen/localization/ncep/pgen/xslt/prod/f000.xslt b/ncep/gov.noaa.nws.ncep.ui.pgen/localization/ncep/pgen/xslt/prod/f000.xslt
index 6f868a9243..887d46481c 100644
--- a/ncep/gov.noaa.nws.ncep.ui.pgen/localization/ncep/pgen/xslt/prod/f000.xslt
+++ b/ncep/gov.noaa.nws.ncep.ui.pgen/localization/ncep/pgen/xslt/prod/f000.xslt
@@ -12,6 +12,7 @@
B. Yin/Chugach 08/11 Initial Coding
B. Yin/SGT 02/14 Added contour support
+ P.Swamy 05/2014 TTR 993 - Highs and Lows do not decode for WPC SFC text product
-->
@@ -35,19 +36,19 @@
-
+
COLD WK
-
+
WARM WK
-
+
STNRY WK
-
+
OCFNT WK
-
+
TROF
diff --git a/ncep/gov.noaa.nws.ncep.ui.pgen/localization/ncep/pgen/xslt/prod/f000_hires.xslt b/ncep/gov.noaa.nws.ncep.ui.pgen/localization/ncep/pgen/xslt/prod/f000_hires.xslt
index 4afc2d2c0e..1f2728f5d4 100644
--- a/ncep/gov.noaa.nws.ncep.ui.pgen/localization/ncep/pgen/xslt/prod/f000_hires.xslt
+++ b/ncep/gov.noaa.nws.ncep.ui.pgen/localization/ncep/pgen/xslt/prod/f000_hires.xslt
@@ -11,6 +11,7 @@
Change Log:
B. Yin/Chugach 08/11 Initial Coding
+ P.Swamy 05/2014 TTR 993 - Highs and Lows do not decode for WPC SFC text product
-->
@@ -33,19 +34,19 @@
-
- COLD
+
+ COLD WK
-
- WARM
+
+ WARM WK
-
- STNRY
+
+ STNRY WK
-
- OCFNT
+
+ OCFNT WK
-
+
TROF
diff --git a/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/Activator.java b/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/Activator.java
index 987de16012..8150e4104c 100644
--- a/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/Activator.java
+++ b/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/Activator.java
@@ -85,6 +85,7 @@ public class Activator extends AbstractUIPlugin {
myprefs.setDefault(PgenPreferences.P_COMP_COORD,
PgenPreferences.CED_COMP_COORD);
myprefs.setDefault(PgenPreferences.P_AUTOPLACE_TEXT, false);
+ myprefs.setDefault(PgenPreferences.P_AUTOPLACE_CONTOUR_LABEL, false);
myprefs.setDefault(PgenPreferences.P_LAYER_MERGE, 4);
}
diff --git a/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/PgenPreferences.java b/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/PgenPreferences.java
index 157c08a9ef..9055b0d11b 100644
--- a/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/PgenPreferences.java
+++ b/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/PgenPreferences.java
@@ -40,6 +40,7 @@ import org.eclipse.ui.IWorkbenchPreferencePage;
* 04/12 #977 S. Gilbert PGEN Database support
* 11/13 TTR752 J. Wu Added P_AUTOPLACE_TEXT
* 12/13 TTR776 J. Wu Added P_LAYER_MERGE
+ * 05/14 TTR 995 J. Wu Added P_AUTOPLACE_CONTOUR_LABEL.
*
*
*
@@ -84,6 +85,11 @@ public class PgenPreferences extends FieldEditorPreferencePage implements
private BooleanFieldEditor autoPlaceText;
+ // Preference to place text box automatically (CCFP);
+ public final static String P_AUTOPLACE_CONTOUR_LABEL = "PGEN_AUTOPLACE_CONTOUR_LABEL";
+
+ private BooleanFieldEditor autoPlaceContourLabel;
+
public final static String P_LAYER_MERGE = "P_LAYER_MERGE";
private BooleanFieldEditor layerLink;
@@ -142,10 +148,16 @@ public class PgenPreferences extends FieldEditorPreferencePage implements
this.addField(projCombo);
autoPlaceText = new BooleanFieldEditor(P_AUTOPLACE_TEXT,
- "&Text Auto Placement (where applicable)",
+ "&Enable Auto Placement for CCFP Text Boxes",
BooleanFieldEditor.DEFAULT, getFieldEditorParent());
this.addField(autoPlaceText);
+ autoPlaceContourLabel = new BooleanFieldEditor(
+ P_AUTOPLACE_CONTOUR_LABEL,
+ "&Enable Auto Placement for Contour Labels",
+ BooleanFieldEditor.DEFAULT, getFieldEditorParent());
+ this.addField(autoPlaceContourLabel);
+
ComboFieldEditor layerMerge = new ComboFieldEditor(P_LAYER_MERGE,
"&Default Action for PGEN Layer Merge:", new String[][] {
{ "Take no action", "0" },
diff --git a/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/PgenUtil.java b/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/PgenUtil.java
index d871cb5a9a..c6488c3c11 100644
--- a/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/PgenUtil.java
+++ b/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/PgenUtil.java
@@ -140,6 +140,8 @@ import com.vividsolutions.jts.linearref.LocationIndexedLine;
* 07/26 TTR J. Wu Extract "DEL_PART" in DeletePartCommand into deleteLinePart().
* 12/13 #1089 B. Yin Removed the UTC time functions to a new class.
* 12/13 #1091 J. Wu Added getLayerMergeOption()
+ * 05/14 TTR 995 J. Wu Added getContourLabelAutoPlacement().
+ * 05/14 TTR998 J. Wu Added pixelToLatlon().
*
*
*
@@ -825,7 +827,7 @@ public class PgenUtil {
* @param pts
* An array of points in lat/lon coordinates
* @param mapDescriptor
- * Descriptoer to use for world to pixel transform
+ * Descriptor to use for world to pixel transform
* @return The array of points in pixel coordinates
*/
public static final double[][] latlonToPixel(Coordinate[] pts,
@@ -843,6 +845,27 @@ public class PgenUtil {
return pixels;
}
+ /**
+ * Converts an array of point in pixel coordinates to lat/lons
+ *
+ * @param pixels
+ * An array of points in pixel coordinates
+ * @param mapDescriptor
+ * Descriptor to use for world to pixel transform
+ * @return The array list of points in lat/lon coordinates
+ */
+ public static final ArrayList pixelToLatlon(double[][] pixels,
+ IMapDescriptor mapDescriptor) {
+ ArrayList crd = new ArrayList();
+
+ for (int ii = 0; ii < pixels.length; ii++) {
+ double[] pt = mapDescriptor.pixelToWorld(pixels[ii]);
+ crd.add(new Coordinate(pt[0], pt[1]));
+ }
+
+ return crd;
+ }
+
/**
* Gets the current site id from the localization Manager
*
@@ -2344,7 +2367,7 @@ public class PgenUtil {
}
/**
- * Returns text auto placement flag
+ * ReturnsCCFP text box auto placement flag
*
* @return
*/
@@ -2353,6 +2376,16 @@ public class PgenUtil {
return prefs.getBoolean(PgenPreferences.P_AUTOPLACE_TEXT);
}
+ /**
+ * Returns contour's label auto placement flag
+ *
+ * @return
+ */
+ public static boolean getContourLabelAutoPlacement() {
+ IPreferenceStore prefs = Activator.getDefault().getPreferenceStore();
+ return prefs.getBoolean(PgenPreferences.P_AUTOPLACE_CONTOUR_LABEL);
+ }
+
/**
* Returns the default PGEN layer merge option.
*
diff --git a/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/attrdialog/ContoursAttrDlg.java b/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/attrdialog/ContoursAttrDlg.java
index 1c1054a174..4a2afcd8ed 100644
--- a/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/attrdialog/ContoursAttrDlg.java
+++ b/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/attrdialog/ContoursAttrDlg.java
@@ -113,6 +113,10 @@ import com.vividsolutions.jts.geom.Coordinate;
* similar changes to labels for other contour objects
* caused a similar error (label or object or both
* would disappear), this should now be fixed.
+ * 05/02/2014 ? D. Sushon In testing solution for trac 1132, it was found that
+ * multiple instances of the contour dialog's Edit
+ * windows could be created with no way to remove,
+ * should now be fixed.
* 05/14 TTR1008 J. Wu Set default contour parameters through settings_tbl.xml.
*
*
@@ -1960,6 +1964,16 @@ public class ContoursAttrDlg extends AttrDlg implements IContours,
}
+ @Override
+ public int open() {
+
+ if (this.getShell() == null || this.getShell().isDisposed()) {
+ return super.open();
+ } else {
+ return CANCEL;
+ }
+ }
+
/**
* Update the label attributes
*/
@@ -1994,6 +2008,11 @@ public class ContoursAttrDlg extends AttrDlg implements IContours,
this.close();
}
+ @Override
+ public void handleShellCloseEvent() {
+ this.close();
+ }
+
/**
* disable un-used widgets
*/
@@ -2260,6 +2279,11 @@ public class ContoursAttrDlg extends AttrDlg implements IContours,
this.close();
}
+ @Override
+ public void handleShellCloseEvent() {
+ this.close();
+ }
+
/**
* disable un-used widgets
*/
@@ -2425,6 +2449,11 @@ public class ContoursAttrDlg extends AttrDlg implements IContours,
this.close();
}
+ @Override
+ public void handleShellCloseEvent() {
+ this.close();
+ }
+
/**
* disable un-used widgets
*/
@@ -3123,6 +3152,16 @@ public class ContoursAttrDlg extends AttrDlg implements IContours,
}
+ @Override
+ public int open() {
+
+ if (this.getShell() == null || this.getShell().isDisposed()) {
+ return super.open();
+ } else {
+ return CANCEL;
+ }
+ }
+
/**
* Update the min/max attributes
*/
@@ -3159,6 +3198,11 @@ public class ContoursAttrDlg extends AttrDlg implements IContours,
this.close();
}
+ @Override
+ public void handleShellCloseEvent() {
+ this.close();
+ }
+
/**
* initialize dialog
*/
@@ -3902,4 +3946,4 @@ public class ContoursAttrDlg extends AttrDlg implements IContours,
return typeChanged;
}
-}
+}
\ No newline at end of file
diff --git a/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/controls/StoreActivityDialog.java b/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/controls/StoreActivityDialog.java
index c8204c4a2b..31c02152ba 100644
--- a/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/controls/StoreActivityDialog.java
+++ b/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/controls/StoreActivityDialog.java
@@ -49,6 +49,7 @@ import com.raytheon.viz.ui.dialogs.CaveJFACEDialog;
* ------------ ---------- ----------- -----------------------------------
* 03/13 #977 S. Gilbert Initial creation
* 01/14 #1105 J. Wu Pre-fill for each activity info.
+ * 05/14 TTR 963 J. Wu Change activity Info to Activity Label.
*
*
*
@@ -440,7 +441,7 @@ public class StoreActivityDialog extends CaveJFACEDialog {
.getShell(),
"Need More Information",
null,
- "Activity Info field is required.\nPlease enter an appropriate string and then try saving!",
+ "Activity Label field is required.\nPlease enter an appropriate string and then try saving!",
MessageDialog.WARNING, new String[] { "OK" }, 0);
confirmDlg.open();
diff --git a/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/display/DisplayElementFactory.java b/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/display/DisplayElementFactory.java
index a4622ce8e5..d75db7d729 100644
--- a/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/display/DisplayElementFactory.java
+++ b/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/display/DisplayElementFactory.java
@@ -140,6 +140,7 @@ import com.vividsolutions.jts.operation.distance.DistanceOp;
* this class
* 11/13 TTR 752 J. Wu added methods to compute an element's range record.
* 12/13 #1089 B. Yin Modify watch to display county list
+ * 05/14 TTR 995 J. Wu Make contour label auto-placement an option.
*
*
* @author sgilbert
@@ -336,10 +337,6 @@ public class DisplayElementFactory {
list.addAll(createDisplayElementsForLines(de, smoothpts,
paintProps));
- // Draw labels for contour lines.
- // list.addAll( adjustContourLineLabels( elem, paintProps,
- // smoothpts ) );
-
}
return list;
@@ -382,7 +379,6 @@ public class DisplayElementFactory {
LinePatternManager lpl = LinePatternManager.getInstance();
try {
pattern = lpl.getLinePattern(de.getPatternName());
- // System.out.println("&&&pattern "+pattern.getMaxExtent());
} catch (LinePatternException lpe) {
/*
* could not find desired line pattern. Used solid line as default.
@@ -623,7 +619,7 @@ public class DisplayElementFactory {
/*
* Draw labels for contour lines.
*/
- list.addAll(adjustContourLineLabels(elem, paintProps, smoothpts));
+ // ???list.addAll(adjustContourLineLabels(elem, paintProps, smoothpts));
return list;
}
@@ -1342,7 +1338,9 @@ public class DisplayElementFactory {
|| tparent instanceof ContourCircle) {
return slist;
} else if (tparent instanceof ContourMinmax) {
- if (((Text) txt).getAuto() != null && ((Text) txt).getAuto()) {
+ boolean forceAuto = PgenUtil.getContourLabelAutoPlacement();
+ if (((Text) txt).getAuto() != null && ((Text) txt).getAuto()
+ || forceAuto) {
Coordinate loc = ((ISinglePoint) ((ContourMinmax) tparent)
.getSymbol()).getLocation();
double[] pixel = iDescriptor.worldToPixel(new double[] {
@@ -1356,6 +1354,11 @@ public class DisplayElementFactory {
pixel[0], pixel[1], 0.0 });
((Text) txt).setLocationOnly(new Coordinate(nloc[0],
nloc[1]));
+ // Only adjust once if auto-place flag in preference is
+ // false.
+ if (!forceAuto) {
+ ((Text) txt).setAuto(false);
+ }
}
}
}
@@ -3093,7 +3096,6 @@ public class DisplayElementFactory {
*/
double psize = pattern.getLength() * sfactor;
double numPatterns = Math.floor(totalDist / psize);
- // System.out.println("NUM_OF_PATTERN_ITERATIONS="+numPatterns+":"+psize);
/*
* Calculate the amount to increase or decrease the pattern length so
@@ -5153,6 +5155,8 @@ public class DisplayElementFactory {
boolean lineClosed = cline.getLine().isClosedLine();
+ boolean forceAuto = PgenUtil.getContourLabelAutoPlacement();
+
/*
* Find the visible part of the line.
*/
@@ -5328,13 +5332,17 @@ public class DisplayElementFactory {
loc[1] = txtPositions.get(kk).y;
tps = iDescriptor.pixelToWorld(loc);
- if (txt.getAuto() != null && txt.getAuto()) {
+ if (txt.getAuto() != null && txt.getAuto() || forceAuto) {
txt.setLocationOnly(new Coordinate(tps[0], tps[1]));
}
txt.setParent(null);
dlist.addAll(createDisplayElements((IText) txt, paintProps));
txt.setParent(cline);
+
+ if (!forceAuto) {
+ txt.setAuto(false);
+ }
}
}
@@ -5356,7 +5364,9 @@ public class DisplayElementFactory {
Text labelText = ((ContourCircle) parent).getLabel();
- if (labelText.getAuto() != null && labelText.getAuto()) {
+ boolean forceAuto = PgenUtil.getContourLabelAutoPlacement();
+
+ if (labelText.getAuto() != null && labelText.getAuto() || forceAuto) {
/*
* Find the visible part of the circle.
*/
@@ -5409,6 +5419,10 @@ public class DisplayElementFactory {
dlist.addAll(createDisplayElements((IText) labelText, paintProps));
labelText.setParent(parent);
+ if (!forceAuto) {
+ labelText.setAuto(false);
+ }
+
}
return dlist;
@@ -5455,8 +5469,7 @@ public class DisplayElementFactory {
spdCoors.get(0),
gov.noaa.nws.ncep.ui.pgen.display.IVector.VectorType.ARROW,
10, vDir, 1.0, false, "Vector", "Arrow");
- // System.out.println("generate a text for " + spd + " at: "
- // + getCcfpTxtPts(v).x + "," + getCcfpTxtPts(v).y);
+
Text spdTxt = new Text(null, "Courier",
14.0f,
TextJustification.CENTER,// .LEFT_JUSTIFY,
@@ -5582,13 +5595,10 @@ public class DisplayElementFactory {
*/
public PgenRangeRecord findTextBoxRange(IText txt,
PaintProperties paintProps) {
- // System.out.println("findTextBoxRange for IText .... enter ");
setScales(paintProps);
double[] tmp = { txt.getPosition().x, txt.getPosition().y, 0.0 };
- // System.out.println("findTextBoxRange for IText .... "
- // + txt.getPosition().x + "," + txt.getPosition().y);
double[] loc = iDescriptor.worldToPixel(tmp);
double horizRatio = paintProps.getView().getExtent().getWidth()
@@ -5601,7 +5611,6 @@ public class DisplayElementFactory {
*/
IFont font = initializeFont(txt.getFontName(), txt.getFontSize(),
txt.getStyle());
- // System.out.println("findTextBoxRange for IText .... 1 ");
/*
* apply X offset in half-characters
@@ -5638,14 +5647,12 @@ public class DisplayElementFactory {
((Text) txt).setXOffset(0);
((Text) txt).setYOffset(0);
}
- // System.out.println("findTextBoxRange for IText .... 2 ");
/*
* Get text color
*/
Color clr = getDisplayColor(txt.getTextColor());
RGB textColor = new RGB(clr.getRed(), clr.getGreen(), clr.getBlue());
- // System.out.println("findTextBoxRange for IText .... 2.5 ");
/*
* Get angle rotation for text. If rotation is "North" relative,
@@ -5654,7 +5661,6 @@ public class DisplayElementFactory {
double rotation = txt.getRotation();
if (txt.getRotationRelativity() == TextRotation.NORTH_RELATIVE)
rotation += northOffsetAngle(txt.getPosition());
- // System.out.println("findTextBoxRange for IText .... 3 ");
/*
* create drawableString and calculate its bounds
@@ -5666,13 +5672,10 @@ public class DisplayElementFactory {
dstring.horizontalAlignment = HorizontalAlignment.CENTER;
dstring.verticallAlignment = VerticalAlignment.MIDDLE;
dstring.rotation = rotation;
- // System.out.println("findTextBoxRange for IText .... 3.1 ");
Rectangle2D bounds = target.getStringsBounds(dstring);
- // System.out.println("findTextBoxRange for IText .... 3.1.1 ");
double xOffset = (bounds.getWidth() + 1) * horizRatio / 2;
double yOffset = (bounds.getHeight() + 1) * vertRatio / 2;
- // System.out.println("findTextBoxRange for IText .... 3.2 ");
/*
* Set proper alignment
@@ -5699,7 +5702,6 @@ public class DisplayElementFactory {
break;
}
}
- // System.out.println("findTextBoxRange for IText .... 4 ");
dstring.horizontalAlignment = align;
@@ -5711,7 +5713,6 @@ public class DisplayElementFactory {
IExtent box = new PixelExtent(dstring.basics.x - left, dstring.basics.x
+ right, dstring.basics.y - yOffset, dstring.basics.y + yOffset);
- // System.out.println("findTextBoxRange for IText .... 5 ");
List rngBox = new ArrayList();
rngBox.add(new Coordinate(box.getMinX() - PgenRangeRecord.RANGE_OFFSET,
@@ -5726,8 +5727,6 @@ public class DisplayElementFactory {
List textPos = new ArrayList();
textPos.add(new Coordinate(loc[0], loc[1]));
- // System.out.println("findTextBoxRange for IText .... return ");
-
return new PgenRangeRecord(rngBox, textPos, false);
}
diff --git a/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/file/ProductConverter.java b/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/file/ProductConverter.java
index 86798de25b..4c2fb4e4a8 100644
--- a/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/file/ProductConverter.java
+++ b/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/file/ProductConverter.java
@@ -133,6 +133,7 @@ import com.vividsolutions.jts.geom.Coordinate;
* 11/13 #1049 B. Yin Handle outlook type defined in layer.
* 12/13 TTR904 B. Yin Added back the water zone string for Watch county list
* 11/13 #1065 J. Wu Added Kink lines.
+ * 05/14 TTR995 J. Wu Set Text's 'auto" flag to false.
*
*
*
@@ -367,10 +368,11 @@ public class ProductConverter {
text.setHide(fText.isHide());
}
- if (fText.isAuto() != null) {
- text.setAuto(fText.isAuto());
- }
-
+ /*
+ * if (fText.isAuto() != null) { //
+ * text.setAuto(fText.isAuto()); text.setAuto(false); }
+ */
+ text.setAuto(false);
des.add(text);
}
diff --git a/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/gfa/GfaClip.java b/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/gfa/GfaClip.java
index 30a4e6920c..cd70686001 100644
--- a/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/gfa/GfaClip.java
+++ b/ncep/gov.noaa.nws.ncep.ui.pgen/src/gov/noaa/nws/ncep/ui/pgen/gfa/GfaClip.java
@@ -36,7 +36,6 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
-//import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
@@ -54,6 +53,8 @@ import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.geom.impl.CoordinateArraySequence;
+//import org.apache.log4j.Logger;
+
/**
* GFA clipping functionality.
*
@@ -82,2178 +83,2235 @@ import com.vividsolutions.jts.geom.impl.CoordinateArraySequence;
* 07/11 #450 G. Hull NcPathManager
* 02/12 #597 S. Gurung Moved snap functionalities to SnapUtil from SigmetInfo.
* 10/12 J. Wu Fix a special case in getRegionIntersectionPt().
+ * 05/14 J. Wu Fix CAVE hang-up when polygon outside of international
+ * bound.
*
*
*
* @author M.Laryukhin
* @version 1
*/
-public class GfaClip {
+public class GfaClip {
- private static GfaClip instance = new GfaClip();
-
-// private final static Logger logger = Logger.getLogger( GfaClip.class );
+ private static GfaClip instance = new GfaClip();
- /** Factory */
- private static GeometryFactory geometryFactory;
+ // private final static Logger logger = Logger.getLogger( GfaClip.class );
- /** Database name, currently defaulted to "ncep". */
- public final String DATABASE = "ncep";
+ /** Factory */
+ private static GeometryFactory geometryFactory;
- /** Schema name, currently defaulted to "bounds". */
- public final String SCHEMA = "bounds";
+ /** Database name, currently defaulted to "ncep". */
+ public final String DATABASE = "ncep";
- public final String FA_REGION_TABLE = "fa_region";
- public final String FA_AREA_TABLE = "fa_area";
- public final String STATE_BNDS_TABLE = "statebnds";
- public final String GREATE_LAKE_BNDS_TABLE = "greatlakesbnds";
- public final String COASTAL_WATER_BNDS_TABLE = "airmetcstlbnds";
- public final String FA_AREAX_TABLE = "fa_areax";
-
- /**
- * The states listed in Raytheon's database but we do not need for GFA.
- * Note: the first entry in Reytheon's state list is a "null" one
- * which is part of Canada.
- */
- private final String[] EXCLUDE_STATES = new String[]{ "AK", "HI", "UM", "GU", "AS", "PR", "VI" };
-// private final String[] WRONG_STATES = new String[] {"VT", "ID", "OH", "FL", "WA", "AK", "HI", "UM", "GU", "AS", "PR", "VI"};
-// private final String[] WRONG_STATES = new String[] {"VT", "ID", "OH", "FL", "WA"}; //OB11.5
- private final String[] WRONG_STATES = new String[] {"HI","VI","ME","VA","MI","MD","MA","AS","AR",
- "IL","MN", "MS","NJ","PR","AK","AL","TX","NC","ND","NY","OK","OH",
- "FL","SD","SC","WI","LA","GU","WA" }; //OB11.7
-
- /**
- * The union of three FA region bound shapes.
- */
- private Geometry faInternationalBound;
- private Geometry faInternationalBoundInGrid;
-
- /**
- * The list of region bound shapes to clip against.
- */
- private HashMap faRegionBounds;
- private HashMap faRegionBoundsInGrid;
+ /** Schema name, currently defaulted to "bounds". */
+ public final String SCHEMA = "bounds";
- /**
- * The list of area bound shapes to clip against.
- */
- private HashMap faAreaBounds;
- private HashMap faAreaBoundsInGrid;
+ public final String FA_REGION_TABLE = "fa_region";
- /**
- * The list of extended area bound shapes to clip against.
- */
- private HashMap faAreaXBounds;
- private HashMap faAreaXBoundsInGrid;
+ public final String FA_AREA_TABLE = "fa_area";
- /**
- * The list of state bound shapes to clip against.
- */
- private HashMap stateBounds;
- private HashMap stateBoundsInGrid;
+ public final String STATE_BNDS_TABLE = "statebnds";
- /**
- * The list of great lake shapes to clip against.
- */
- private HashMap greatLakesBounds;
- private HashMap greatLakesBoundsInGrid;
-
- /**
- * The list of great lake shapes to clip against.
- */
- private HashMap coastalWaterBounds;
- private HashMap coastalWaterBoundsInGrid;
-
- /**
- * The list of region bound shapes to clip against.
- */
- private HashMap faRegionCommBounds;
- private HashMap faRegionCommBoundsInGrid;
+ public final String GREATE_LAKE_BNDS_TABLE = "greatlakesbnds";
- /**
- * The list of region bound shapes to clip against.
- */
- private HashMap faAreaXCommBounds;
- private HashMap faAreaXCommBoundsInGrid;
-
-
- /** State list that allows MT_OBSC */
- private static Document mtObscTbl = null;
-
- /** XPATH for states in document */
- public static String MTOBSC_XPATH = "/MT_OBSC";
-
- /** State list that allows MT_OBSC */
- private static List mtObscStates = null;
-
- /** State list that allows MT_OBSC */
- private static final int NEW_POINT = 2;
- private static final int BOUND_POINT = 1;
- private static final int ORIGINAL_POINT = 0;
-
- /** Tie distance for two points in map coord */
- private static final double SMALLF = (0.01);
-
-
- /**
- * Private constructor, we only need to read the database once.
- */
- private GfaClip() {
- geometryFactory = new GeometryFactory();
- }
+ public final String COASTAL_WATER_BNDS_TABLE = "airmetcstlbnds";
- /**
- * Singleton instance.
- *
- * @return
- */
- public static GfaClip getInstance() {
- return instance;
- }
+ public final String FA_AREAX_TABLE = "fa_areax";
- /**
- * Clips to regions.
- *
- * @param smear
- * @return
- */
- public ArrayList simpleclip(Gfa smear) {
-
-// logger.debug("clipping started");
-
- ArrayList list = new ArrayList();
- for (String key : getFaRegionBounds().keySet()) {
- Geometry region = faRegionBounds.get(key);
+ /**
+ * The states listed in Raytheon's database but we do not need for GFA.
+ * Note: the first entry in Reytheon's state list is a "null" one which is
+ * part of Canada.
+ */
+ private final String[] EXCLUDE_STATES = new String[] { "AK", "HI", "UM",
+ "GU", "AS", "PR", "VI" };
- Polygon polygon = gfaToPolygon(smear);
+ // private final String[] WRONG_STATES = new String[] {"VT", "ID", "OH",
+ // "FL", "WA", "AK", "HI", "UM", "GU", "AS", "PR", "VI"};
+ // private final String[] WRONG_STATES = new String[] {"VT", "ID", "OH",
+ // "FL", "WA"}; //OB11.5
+ private final String[] WRONG_STATES = new String[] { "HI", "VI", "ME",
+ "VA", "MI", "MD", "MA", "AS", "AR", "IL", "MN", "MS", "NJ", "PR",
+ "AK", "AL", "TX", "NC", "ND", "NY", "OK", "OH", "FL", "SD", "SC",
+ "WI", "LA", "GU", "WA" }; // OB11.7
- Geometry intersection = null;
- if (region.intersects(polygon)) {
- intersection = region.intersection(polygon);
- if (intersection instanceof MultiPolygon) {
- MultiPolygon mp = (MultiPolygon) intersection;
- for (int i = 0; i < mp.getNumGeometries(); i++) {
- Gfa g = geometryToGfa(smear, mp.getGeometryN(i));
- g.addNotToBeSnapped(region.getCoordinates());
- list.add(g);
- }
- } else {
- Gfa g = geometryToGfa(smear, intersection);
- g.addNotToBeSnapped(region.getCoordinates());
- list.add(g);
- }
- } else if (region.covers(polygon)) {
- smear.addNotToBeSnapped(region.getCoordinates());
- list.add(smear);
- }
- }
-
- return list;
- }
+ /**
+ * The union of three FA region bound shapes.
+ */
+ private Geometry faInternationalBound;
-
- /**
- * Generate a JTS polygon from a GFA polygon.
- *
- * @param gfa a GFA element
- * @return
- */
- public Polygon gfaToPolygon( Gfa gfa ) {
- if ( gfa != null ) {
- return pointsToPolygon( gfa.getLinePoints() );
- }
- else {
- return null;
- }
- }
+ private Geometry faInternationalBoundInGrid;
- /**
- * Generate a JTS polygon in grid coordinate from a GFA polygon.
- *
- * @param gfa a GFA element
- * @return
- */
- public Polygon gfaToPolygonInGrid( Gfa gfa ) {
- if ( gfa != null ) {
- return pointsToPolygon( PgenUtil.latlonToGrid( gfa.getLinePoints() ) );
- }
- else {
- return null;
- }
- }
+ /**
+ * The list of region bound shapes to clip against.
+ */
+ private HashMap faRegionBounds;
- /**
- * Generate a JTS polygon from a set of points.
+ private HashMap faRegionBoundsInGrid;
+
+ /**
+ * The list of area bound shapes to clip against.
+ */
+ private HashMap faAreaBounds;
+
+ private HashMap faAreaBoundsInGrid;
+
+ /**
+ * The list of extended area bound shapes to clip against.
+ */
+ private HashMap faAreaXBounds;
+
+ private HashMap faAreaXBoundsInGrid;
+
+ /**
+ * The list of state bound shapes to clip against.
+ */
+ private HashMap stateBounds;
+
+ private HashMap stateBoundsInGrid;
+
+ /**
+ * The list of great lake shapes to clip against.
+ */
+ private HashMap greatLakesBounds;
+
+ private HashMap greatLakesBoundsInGrid;
+
+ /**
+ * The list of great lake shapes to clip against.
+ */
+ private HashMap coastalWaterBounds;
+
+ private HashMap coastalWaterBoundsInGrid;
+
+ /**
+ * The list of region bound shapes to clip against.
+ */
+ private HashMap faRegionCommBounds;
+
+ private HashMap faRegionCommBoundsInGrid;
+
+ /**
+ * The list of region bound shapes to clip against.
+ */
+ private HashMap faAreaXCommBounds;
+
+ private HashMap faAreaXCommBoundsInGrid;
+
+ /** State list that allows MT_OBSC */
+ private static Document mtObscTbl = null;
+
+ /** XPATH for states in document */
+ public static String MTOBSC_XPATH = "/MT_OBSC";
+
+ /** State list that allows MT_OBSC */
+ private static List mtObscStates = null;
+
+ /** State list that allows MT_OBSC */
+ private static final int NEW_POINT = 2;
+
+ private static final int BOUND_POINT = 1;
+
+ private static final int ORIGINAL_POINT = 0;
+
+ /** Tie distance for two points in map coord */
+ private static final double SMALLF = (0.01);
+
+ /**
+ * Private constructor, we only need to read the database once.
+ */
+ private GfaClip() {
+ geometryFactory = new GeometryFactory();
+ }
+
+ /**
+ * Singleton instance.
*
- * It is assumed that the first point is not repeated at the end
- * in the input array of point.
- *
- * @param points array of points
* @return
*/
- public Polygon pointsToPolygon( Coordinate[] points ) {
-
- Coordinate[] coords = Arrays.copyOf( points, points.length + 1 );
- coords[coords.length - 1] = coords[0];
+ public static GfaClip getInstance() {
+ return instance;
+ }
- CoordinateArraySequence cas = new CoordinateArraySequence( coords );
- LinearRing ring = new LinearRing( cas, geometryFactory );
+ /**
+ * Clips to regions.
+ *
+ * @param smear
+ * @return
+ */
+ public ArrayList simpleclip(Gfa smear) {
- Polygon polygon = new Polygon( ring, null, geometryFactory );
-
- return polygon;
- }
+ // logger.debug("clipping started");
+
+ ArrayList list = new ArrayList();
+ for (String key : getFaRegionBounds().keySet()) {
+ Geometry region = faRegionBounds.get(key);
+
+ Polygon polygon = gfaToPolygon(smear);
+
+ Geometry intersection = null;
+ if (region.intersects(polygon)) {
+ intersection = region.intersection(polygon);
+ if (intersection instanceof MultiPolygon) {
+ MultiPolygon mp = (MultiPolygon) intersection;
+ for (int i = 0; i < mp.getNumGeometries(); i++) {
+ Gfa g = geometryToGfa(smear, mp.getGeometryN(i));
+ g.addNotToBeSnapped(region.getCoordinates());
+ list.add(g);
+ }
+ } else {
+ Gfa g = geometryToGfa(smear, intersection);
+ g.addNotToBeSnapped(region.getCoordinates());
+ list.add(g);
+ }
+ } else if (region.covers(polygon)) {
+ smear.addNotToBeSnapped(region.getCoordinates());
+ list.add(smear);
+ }
+ }
+
+ return list;
+ }
+
+ /**
+ * Generate a JTS polygon from a GFA polygon.
+ *
+ * @param gfa
+ * a GFA element
+ * @return
+ */
+ public Polygon gfaToPolygon(Gfa gfa) {
+ if (gfa != null) {
+ return pointsToPolygon(gfa.getLinePoints());
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Generate a JTS polygon in grid coordinate from a GFA polygon.
+ *
+ * @param gfa
+ * a GFA element
+ * @return
+ */
+ public Polygon gfaToPolygonInGrid(Gfa gfa) {
+ if (gfa != null) {
+ return pointsToPolygon(PgenUtil.latlonToGrid(gfa.getLinePoints()));
+ } else {
+ return null;
+ }
+ }
/**
* Generate a JTS polygon from a set of points.
*
- * @param points array of points
+ * It is assumed that the first point is not repeated at the end in the
+ * input array of point.
+ *
+ * @param points
+ * array of points
* @return
*/
- public Geometry pointsToGeometry( Coordinate[] points ) {
-
- Geometry geom;
- if ( points == null || points.length == 0 ) {
- geom = null;
- }
- else if ( points.length == 1 ) {
- CoordinateArraySequence cas = new CoordinateArraySequence( points );
- geom = new Point( cas, geometryFactory ) ;
- }
- else if ( points.length == 2 ) {
- geom = pointsToLineString( points );
- }
- else {
- geom = pointsToPolygon( points );
- }
-
- return geom;
- }
+ public Polygon pointsToPolygon(Coordinate[] points) {
- /**
+ Coordinate[] coords = Arrays.copyOf(points, points.length + 1);
+ coords[coords.length - 1] = coords[0];
+
+ CoordinateArraySequence cas = new CoordinateArraySequence(coords);
+ LinearRing ring = new LinearRing(cas, geometryFactory);
+
+ Polygon polygon = new Polygon(ring, null, geometryFactory);
+
+ return polygon;
+ }
+
+ /**
* Generate a JTS polygon from a set of points.
*
- * @param pots list of points
+ * @param points
+ * array of points
* @return
*/
- public Geometry pointsToGeometry( ArrayList pts ) {
-
- Coordinate[] points = new Coordinate[ pts.size() ];
- pts.toArray( points );
-
- return pointsToGeometry( points );
- }
-
-
+ public Geometry pointsToGeometry(Coordinate[] points) {
+
+ Geometry geom;
+ if (points == null || points.length == 0) {
+ geom = null;
+ } else if (points.length == 1) {
+ CoordinateArraySequence cas = new CoordinateArraySequence(points);
+ geom = new Point(cas, geometryFactory);
+ } else if (points.length == 2) {
+ geom = pointsToLineString(points);
+ } else {
+ geom = pointsToPolygon(points);
+ }
+
+ return geom;
+ }
+
+ /**
+ * Generate a JTS polygon from a set of points.
+ *
+ * @param pots
+ * list of points
+ * @return
+ */
+ public Geometry pointsToGeometry(ArrayList pts) {
+
+ Coordinate[] points = new Coordinate[pts.size()];
+ pts.toArray(points);
+
+ return pointsToGeometry(points);
+ }
+
/**
* Generate a JTS LineString from a set of points.
*
- * @param points array of points
+ * @param points
+ * array of points
* @return
*/
- public Geometry pointsToLineString( Coordinate[] points ) {
-
- CoordinateArraySequence cas = new CoordinateArraySequence( points );
-
- return new LineString( cas, geometryFactory );
- }
-
- /**
- * Create a new GFA from an input GFA with points in a given Geometry.
- *
- * Note: the input geometry should not have holes in it.
- *
- * @param gfaIn
- * @param geomIn
- * @return
- */
- public Gfa geometryToGfa( Gfa gfaIn, Geometry geomIn ) {
-
- Coordinate[] c = geomIn.getCoordinates();
- ArrayList coor = new ArrayList();
-
- coor.addAll( Arrays.asList( c ) );
- coor.remove( coor.size() - 1 ); // remove last
-
- Gfa g = gfaIn.copy();
-
- g.setPoints( coor );
- g.setGfaTextCoordinate( g.getCentroid() );
-
- return g;
- }
+ public Geometry pointsToLineString(Coordinate[] points) {
- /**
- * Creates the bounds polygon.
- *
- * @throws VizException
- */
- private void readFaRegionBounds() throws VizException {
- faRegionBounds = new HashMap();
- for ( FARegion fa : PgenStaticDataProvider.getProvider().getFARegions()){
- faRegionBounds.put(fa.getRegion(), fa.getGeometry());
- }
- loadFaRegionCommBounds();
- }
-
- /**
- * Creates the FA area bounds polygon.
- *
- * @throws VizException
- */
- private void readFaAreaBounds() throws VizException {
- faAreaBounds = new HashMap();
- for ( FAArea fa : PgenStaticDataProvider.getProvider().getFAAreas()){
- faAreaBounds.put(fa.getArea(), fa.getGeometry());
- }
- }
-
- /**
- * Creates the extended FA area bounds polygon.
- *
- * @throws VizException
- */
- private void readFaAreaXBounds() throws VizException {
- faAreaXBounds = new HashMap();
- for ( FAArea fa : PgenStaticDataProvider.getProvider().getFAAreaX()){
- faAreaXBounds.put(fa.getArea(), fa.getGeometry());
- }
- }
-
- /**
- * Creates the state bounds polygon - only include States used for GFA.
- *
- * @throws VizException
- */
- private void readStateBounds() throws VizException {
- // TODO change to use SCHEMA, DATABASE, and STATE_BNDS_TABLE
-// String sql = "select t.state, AsBinary(t.the_geom) from mapdata.states t";
-// String sql = "select t.state, AsBinary(t.the_geom_0) from mapdata.states t";
-// String sql = "select t.state, AsBinary(t.the_geom_0_001) from mapdata.states t";
-// String sql = "select t.state, AsBinary(t.the_geom_0_004) from mapdata.states t";
-// String sql = "select t.state, AsBinary(t.the_geom_0_016) from mapdata.states t";
-// String sql = "select t.state, AsBinary(t.the_geom_0_064) from mapdata.states t";
-
-// long l1 = System.currentTimeMillis();
-// stateBounds = readBounds( "maps", sql );
-// System.out.println("Time to load state bounds = " + (System.currentTimeMillis() - l1 ) );
+ CoordinateArraySequence cas = new CoordinateArraySequence(points);
-
- HashMap originalStateBounds = new HashMap();
-
- for ( USState st : PgenStaticDataProvider.getProvider().getAllstates()){
- originalStateBounds.put(st.getStateAbrv(), st.getShape());
- }
-
- //Exclude a few states.
- for ( String s : EXCLUDE_STATES ) {
- originalStateBounds.remove( s );
- }
-
- stateBounds = fixStateBounds( originalStateBounds );
-
- }
-
- /**
- * Creates the great Lakes bounds polygon.
- *
- * @throws VizException
- */
- private void readGreatLakeBounds() throws VizException {
- greatLakesBounds = new HashMap();
- for ( GreatLake lake : PgenStaticDataProvider.getProvider().getGreatLakes()){
- greatLakesBounds.put(lake.getId(), lake.getGeometry());
- }
- }
-
- /**
- * Creates the coastal water bounds polygon.
- *
- * @throws VizException
- */
- private void readCoastalWaterBounds() throws VizException {
- coastalWaterBounds = new HashMap();
- for ( CostalWater water : PgenStaticDataProvider.getProvider().getCostalWaters()){
- coastalWaterBounds.put(water.getId(), water.getGeometry());
- }
- }
+ return new LineString(cas, geometryFactory);
+ }
- /**
- * Load FA Region bounds into a HashMap (West, Central, East)
- * @return
- */
- public HashMap getFaRegionBounds() {
- if ( faRegionBounds == null ){
- try {
- readFaRegionBounds();
- } catch ( VizException e) {
-// logger.error("Error ", e);
- e.printStackTrace();
- }
- }
-
- return faRegionBounds;
- }
+ /**
+ * Create a new GFA from an input GFA with points in a given Geometry.
+ *
+ * Note: the input geometry should not have holes in it.
+ *
+ * @param gfaIn
+ * @param geomIn
+ * @return
+ */
+ public Gfa geometryToGfa(Gfa gfaIn, Geometry geomIn) {
- /**
- * Load FA Area bounds into a HashMap (SLC, SFO, CHI, DFW, BOS, MIA)
- * @return
- */
- public HashMap getFaAreaBounds() {
- if ( faAreaBounds == null ) {
- try {
- readFaAreaBounds();
- } catch ( VizException e ) {
-// logger.error("Error ", e);
- e.printStackTrace();
- }
- }
-
- return faAreaBounds;
- }
+ Coordinate[] c = geomIn.getCoordinates();
+ ArrayList coor = new ArrayList();
- /**
- * Load extended FA Area bounds into a HashMap (SLC, SFO, CHI, DFW, BOS, MIA)
- * @return
- */
- public HashMap getFaAreaXBounds() {
- if ( faAreaXBounds == null ) {
- try {
- readFaAreaXBounds();
- } catch ( VizException e ) {
-// logger.error("Error ", e);
- e.printStackTrace();
- }
- }
+ coor.addAll(Arrays.asList(c));
+ coor.remove(coor.size() - 1); // remove last
- return faAreaXBounds;
- }
+ Gfa g = gfaIn.copy();
- /**
- * Load state bounds into a HashMap keyed by state name
- *
- * @return
- */
- public HashMap getStateBounds() {
- if ( stateBounds == null ) {
- try {
- readStateBounds();
- } catch ( VizException e ) {
-// logger.error("Error ", e);
- e.printStackTrace();
- }
- }
-
- return stateBounds;
- }
-
- /**
- * Load Great Lake bounds into a HashMap
- * @return
- */
- public HashMap getGreatLakeBounds() {
- if ( greatLakesBounds == null ) {
- try {
- readGreatLakeBounds();
- } catch ( VizException e) {
-// logger.error("Error ", e);
- e.printStackTrace();
- }
- }
-
- return greatLakesBounds;
- }
-
- /**
- * Load Coastal Water bounds into a HashMap
- * @return
- */
- public HashMap getCoastalWaterBounds() {
- if ( coastalWaterBounds == null ) {
- try {
- readCoastalWaterBounds();
- } catch ( VizException e ) {
-// logger.error("Error ", e);
- e.printStackTrace();
- }
- }
-
- return coastalWaterBounds;
- }
-
- /**
- * Create a union of three FA Region bounds (international bound)
- * @return
- */
- public Geometry getFaInternationalBound() {
-
- if ( faInternationalBound == null ) {
- ArrayList rbnds = new ArrayList( getFaRegionBounds().values() );
- faInternationalBound = quickUnion( rbnds );
- }
+ g.setPoints(coor);
+ g.setGfaTextCoordinate(g.getCentroid());
- return faInternationalBound;
- }
-
- /**
- * Create a union of a list of Geometries.
- *
- * This method uses GeometryCollection's buffer method to union
- * a collection of Geometries, which is believed to be faster than
- * using Geometry's union method to union geometries one by one,
- * according to JTS documentation.
- *
- * @param geoms A list of geometries
- * @return
- */
- public Geometry quickUnion( ArrayList geoms ) {
-
- if ( geoms.size() <= 0 ) return null;
-
- Geometry[] regs = new Geometry[ geoms.size() ];
- regs = geoms.toArray( regs );
-
- GeometryCollection gc = new GeometryCollection( regs, geometryFactory );
-
- return gc.buffer( 0.0 );
-
- }
+ return g;
+ }
-
- /**
- * Clip to FA regions.
- *
- * This routine clips a non-FZLVL GFA airmet/outlook against FA region
+ /**
+ * Creates the bounds polygon.
+ *
+ * @throws VizException
+ */
+ private void readFaRegionBounds() throws VizException {
+ faRegionBounds = new HashMap();
+ for (FARegion fa : PgenStaticDataProvider.getProvider().getFARegions()) {
+ faRegionBounds.put(fa.getRegion(), fa.getGeometry());
+ }
+ loadFaRegionCommBounds();
+ }
+
+ /**
+ * Creates the FA area bounds polygon.
+ *
+ * @throws VizException
+ */
+ private void readFaAreaBounds() throws VizException {
+ faAreaBounds = new HashMap();
+ for (FAArea fa : PgenStaticDataProvider.getProvider().getFAAreas()) {
+ faAreaBounds.put(fa.getArea(), fa.getGeometry());
+ }
+ }
+
+ /**
+ * Creates the extended FA area bounds polygon.
+ *
+ * @throws VizException
+ */
+ private void readFaAreaXBounds() throws VizException {
+ faAreaXBounds = new HashMap();
+ for (FAArea fa : PgenStaticDataProvider.getProvider().getFAAreaX()) {
+ faAreaXBounds.put(fa.getArea(), fa.getGeometry());
+ }
+ }
+
+ /**
+ * Creates the state bounds polygon - only include States used for GFA.
+ *
+ * @throws VizException
+ */
+ private void readStateBounds() throws VizException {
+ // TODO change to use SCHEMA, DATABASE, and STATE_BNDS_TABLE
+ // String sql =
+ // "select t.state, AsBinary(t.the_geom) from mapdata.states t";
+ // String sql =
+ // "select t.state, AsBinary(t.the_geom_0) from mapdata.states t";
+ // String sql =
+ // "select t.state, AsBinary(t.the_geom_0_001) from mapdata.states t";
+ // String sql =
+ // "select t.state, AsBinary(t.the_geom_0_004) from mapdata.states t";
+ // String sql =
+ // "select t.state, AsBinary(t.the_geom_0_016) from mapdata.states t";
+ // String sql =
+ // "select t.state, AsBinary(t.the_geom_0_064) from mapdata.states t";
+
+ // long l1 = System.currentTimeMillis();
+ // stateBounds = readBounds( "maps", sql );
+ // System.out.println("Time to load state bounds = " +
+ // (System.currentTimeMillis() - l1 ) );
+
+ HashMap originalStateBounds = new HashMap();
+
+ for (USState st : PgenStaticDataProvider.getProvider().getAllstates()) {
+ originalStateBounds.put(st.getStateAbrv(), st.getShape());
+ }
+
+ // Exclude a few states.
+ for (String s : EXCLUDE_STATES) {
+ originalStateBounds.remove(s);
+ }
+
+ stateBounds = fixStateBounds(originalStateBounds);
+
+ }
+
+ /**
+ * Creates the great Lakes bounds polygon.
+ *
+ * @throws VizException
+ */
+ private void readGreatLakeBounds() throws VizException {
+ greatLakesBounds = new HashMap();
+ for (GreatLake lake : PgenStaticDataProvider.getProvider()
+ .getGreatLakes()) {
+ greatLakesBounds.put(lake.getId(), lake.getGeometry());
+ }
+ }
+
+ /**
+ * Creates the coastal water bounds polygon.
+ *
+ * @throws VizException
+ */
+ private void readCoastalWaterBounds() throws VizException {
+ coastalWaterBounds = new HashMap();
+ for (CostalWater water : PgenStaticDataProvider.getProvider()
+ .getCostalWaters()) {
+ coastalWaterBounds.put(water.getId(), water.getGeometry());
+ }
+ }
+
+ /**
+ * Load FA Region bounds into a HashMap (West, Central, East)
+ *
+ * @return
+ */
+ public HashMap getFaRegionBounds() {
+ if (faRegionBounds == null) {
+ try {
+ readFaRegionBounds();
+ } catch (VizException e) {
+ // logger.error("Error ", e);
+ e.printStackTrace();
+ }
+ }
+
+ return faRegionBounds;
+ }
+
+ /**
+ * Load FA Area bounds into a HashMap (SLC, SFO, CHI, DFW, BOS, MIA)
+ *
+ * @return
+ */
+ public HashMap getFaAreaBounds() {
+ if (faAreaBounds == null) {
+ try {
+ readFaAreaBounds();
+ } catch (VizException e) {
+ // logger.error("Error ", e);
+ e.printStackTrace();
+ }
+ }
+
+ return faAreaBounds;
+ }
+
+ /**
+ * Load extended FA Area bounds into a HashMap (SLC, SFO, CHI, DFW, BOS,
+ * MIA)
+ *
+ * @return
+ */
+ public HashMap getFaAreaXBounds() {
+ if (faAreaXBounds == null) {
+ try {
+ readFaAreaXBounds();
+ } catch (VizException e) {
+ // logger.error("Error ", e);
+ e.printStackTrace();
+ }
+ }
+
+ return faAreaXBounds;
+ }
+
+ /**
+ * Load state bounds into a HashMap keyed by state name
+ *
+ * @return
+ */
+ public HashMap getStateBounds() {
+ if (stateBounds == null) {
+ try {
+ readStateBounds();
+ } catch (VizException e) {
+ // logger.error("Error ", e);
+ e.printStackTrace();
+ }
+ }
+
+ return stateBounds;
+ }
+
+ /**
+ * Load Great Lake bounds into a HashMap
+ *
+ * @return
+ */
+ public HashMap getGreatLakeBounds() {
+ if (greatLakesBounds == null) {
+ try {
+ readGreatLakeBounds();
+ } catch (VizException e) {
+ // logger.error("Error ", e);
+ e.printStackTrace();
+ }
+ }
+
+ return greatLakesBounds;
+ }
+
+ /**
+ * Load Coastal Water bounds into a HashMap
+ *
+ * @return
+ */
+ public HashMap getCoastalWaterBounds() {
+ if (coastalWaterBounds == null) {
+ try {
+ readCoastalWaterBounds();
+ } catch (VizException e) {
+ // logger.error("Error ", e);
+ e.printStackTrace();
+ }
+ }
+
+ return coastalWaterBounds;
+ }
+
+ /**
+ * Create a union of three FA Region bounds (international bound)
+ *
+ * @return
+ */
+ public Geometry getFaInternationalBound() {
+
+ if (faInternationalBound == null) {
+ ArrayList rbnds = new ArrayList(
+ getFaRegionBounds().values());
+ faInternationalBound = quickUnion(rbnds);
+ }
+
+ return faInternationalBound;
+ }
+
+ /**
+ * Create a union of a list of Geometries.
+ *
+ * This method uses GeometryCollection's buffer method to union a collection
+ * of Geometries, which is believed to be faster than using Geometry's union
+ * method to union geometries one by one, according to JTS documentation.
+ *
+ * @param geoms
+ * A list of geometries
+ * @return
+ */
+ public Geometry quickUnion(ArrayList geoms) {
+
+ if (geoms.size() <= 0)
+ return null;
+
+ Geometry[] regs = new Geometry[geoms.size()];
+ regs = geoms.toArray(regs);
+
+ GeometryCollection gc = new GeometryCollection(regs, geometryFactory);
+
+ return gc.buffer(0.0);
+
+ }
+
+ /**
+ * Clip to FA regions.
+ *
+ * This routine clips a non-FZLVL GFA airmet/outlook against FA region
* boundaries to generates a list of clipped smears.
*
* The algorithm is as following:
*
- * 1. Check if the smear is big enough ( >= 3K sq. nautical miles )
- * 2. Clip against the international boundary and check if the intersection is big enough
- * 3. Clip against FA region boundaries -
- * 3.1 clip the smear polygon against international boundary.
- * 3.2 clip the result against each FA region (WEST, CENTRAL, EAST)
- * 3.3 create a list of parts bigger than/equal to 3K for each region
- * 3.4 hold the parts smaller than 3K for all regions in another list.
- * 4. Create smears from VALID parts and add into the return list
- * 4.1 Union the parts in each FA region with the list of small parts -
- * If a small part touches one of the bigs (only one, I believe), it
- * will be united into that big and thus be included into a new
- * smear. So no part gets lost and all will be included eventually.
- * 4.2 Check the resulting union for VALID geometries and create new
- * smears for each of them.
- *
- * A VALID geometry is one that
- * a. is large enough ( >= 3K sq. nautical miles )
- * b. intersects at least one of its associated snapshots (from
- * which the smear polygon is generated) with an area >= 3K.
- * c. intersects snapshot "6" with an area >= 3K, if it is a "6-6" smear
- * and there are snapshots associated with it.
- *
- * @param smear the GFA smear to be clipped
- * @param snapshots snapshots from where the input smear is created
- * @return a list of clipped GFA smears.
- */
- public ArrayList clipFARegions( Gfa smear, ArrayList snapshots ) {
-
- ArrayList clippedList = new ArrayList();
- if ( smear.getGfaHazard().equals( "FZLVL" ) ) {
- return clippedList;
- }
-
- /*
- * If the smear size < AREA_LIMIT, stop.
- */
- if ( !isBiggerInMap( gfaToPolygon( smear ) ) ) {
- return clippedList;
- }
-
- /*
- * Do international clipping (in grid coordinate)
- */
- boolean _clippingFlg = true;
-
- Geometry intlBound = getFaInternationalBound();
- Geometry intlBoundInGrid = getFaInternationalBoundInGrid() ;
-
- Polygon smearPoly = gfaToPolygon( smear );
- Polygon smearPolyInGrid = pointsToPolygon( PgenUtil.latlonToGrid( smear.getLinePoints() ) );
-
- Geometry clipAgstIntlBnd = null;
-
- if ( smearPolyInGrid.intersects( intlBoundInGrid ) ) {
- clipAgstIntlBnd = smearPolyInGrid.intersection( intlBoundInGrid );
- }
-
- if ( clipAgstIntlBnd == null || clipAgstIntlBnd.getNumGeometries() <= 0 ||
- !isBiggerInGrid( clipAgstIntlBnd ) ) {
- return clippedList;
- }
-
-
- /*
- * Find intersection points with the international bound and the common borders
- * between West and Central, Central and East regions. Then insert and snap them
- * for later replacement.
- */
- HashMap intlPts = getIntlIntersectionPt( smearPoly, intlBound, clipAgstIntlBnd );
-
- HashMap regionInterPts = getRegionIntersectionPt( smearPoly );
-
- /*
- * Build a list of non-reduce-able point, include all bound points and snapped
- * intersection points
- */
- HashMap rgbnds = getFaRegionBounds();
- HashMap rgbndsInGrid = getFaRegionBoundsInGrid();
-
- CoordinateList nonReduceable = new CoordinateList();
- nonReduceable.addAll( intlPts.values(), false );
- nonReduceable.addAll( regionInterPts.values(), false );
- for ( Geometry gb : rgbnds.values() ) {
- nonReduceable.addAll( Arrays.asList( gb.getCoordinates() ), false );
- }
-
-
- //Build a map of all replaceable points
- HashMap replacePts = new HashMap();
- replacePts.putAll( intlPts );
- replacePts.putAll( regionInterPts );
-
- /*
- * Regional clipping as following:
- *
- * 1. clip the smear polygon against international boundary.
- * 2. clip the result against each FA region (WEST, CENTRAL, EAST)
- * 3. create a list of parts bigger than/equal to 3K for each region
- * 4. hold the parts smaller than 3K for all regions in another list.
- */
- HashMap > clipWithRegions = new HashMap >();
-
- ArrayList smallPoly = new ArrayList();
-
- for ( String regionName : rgbnds.keySet() ) {
-
- clipWithRegions.put( regionName, new ArrayList() );
-
- // Always start from the smear clipped within international bound
- Geometry startPoly = intlBoundInGrid.intersection( smearPolyInGrid );
-
- //Do regional clipping as described above.
- if ( _clippingFlg ) {
-
- Geometry regionBnd = rgbndsInGrid.get( regionName );
-
- if ( regionBnd.intersects( startPoly ) ) {
- Geometry regionPoly = regionBnd.intersection( startPoly );
-
- if ( regionPoly != null ) {
-
- for ( int kk = 0; kk < regionPoly.getNumGeometries(); kk++ ) {
- Geometry bigPoly = regionPoly.getGeometryN( kk );
-
- if ( isBiggerInGrid( bigPoly ) ) {
- clipWithRegions.get( regionName ).add( bigPoly );
- }
- else {
- smallPoly.add( bigPoly );
- }
- }
- }
- }
- }
- }
-
- /*
- * Create smears from VALID parts and add into the return list
- *
- * 1. Union the parts in each FA region with the list of small parts -
- * If a small part touches one of the bigs (only one, I believe), it
- * will be united into that big and thus be included into a new
- * smear. So no part gets lost and all will be included eventually.
- * 2. When a small part is united into a big one (if they intersect),
- * the common points are still retained in the result, we need to
- * remove those extra point. The common points are resulted from the
- * previous clipping with the regional boundaries.
- * 3. check the resulting union for VALID geometries and create new
- * smears for each of them.
- */
- for ( String regionName : clipWithRegions.keySet() ) {
-
- ArrayList bigs = clipWithRegions.get( regionName );
-
- ArrayList toBeUnioned = new ArrayList();
- toBeUnioned.addAll( bigs );
- toBeUnioned.addAll( smallPoly );
- Geometry result = quickUnion( toBeUnioned );
-
- ArrayList commPts = getCommonPoints( bigs, smallPoly );
-
- if ( result != null ) {
- for ( int kk = 0; kk < result.getNumGeometries(); kk++ ) {
-
- Geometry one = result.getGeometryN( kk );
-
- /*
- * Remove residue points due to clipping followed by subsequent union.
- * Done in grid coordinate.
- */
- Geometry onePart = removeCommonPoints( one, commPts );
-
- /*
- * Clean up some improper cases - done in MAP coordinate.
- */
- Coordinate[] gPts = PgenUtil.gridToLatlon( onePart.getCoordinates() );
- ArrayList points = new ArrayList();
- for ( Coordinate c : gPts ) {
- points.add( c );
- }
- points.remove( points.size() - 1 );
-
- Geometry cleanPts = cleanupPoints( pointsToGeometry( points ) );
-
- if ( isAddableAsSmear( cleanPts, snapshots, smear ) ) {
-
- //Replace intersection points with their pre-snapped pair - map
- Geometry rplPts = replacePts( cleanPts, replacePts );
-
- //Clean up duplicate point from point replacement - map
- Geometry finalPts = cleanupPoints( rplPts );
-
- if ( finalPts != null ) {
- //Create a new GFA smear.
- Gfa newElm = geometryToGfa( smear, finalPts );
-
- //Mark non-reduce-able points.
- addReduceFlags( newElm, nonReduceable );
-
- newElm.addAttribute( "FA_REGION", new String(regionName) );
-
- clippedList.add( newElm );
- }
- }
- }
- }
-
- }
-
- return clippedList;
- }
-
-
- /**
- * Check if a polygon is valid to be added as a new smear:
- *
- * Note: the input geometry is assumed to be in map coordinate.
- *
- * 1. it must be large enough ( >= 3K sq. nautical miles )
- * 2. it must intersect at least one of its associated snapshots (from
- * which the smear polygon is generated) with an area >= 3K.
- * 3. if it is "6-6" smear, it must intersect snapshot "6" with an
- * area >= 3K.
- *
- * @param g A Geometry (polygon) in map coordinate.
- * @param snapshots A list of associated snapshots
- * @param smear Original GFA smear
- * @return
- */
- public boolean isAddableAsSmear( Geometry g, ArrayList snapshots, Gfa smear ) {
-
- return ( isBiggerInMap( g ) &&
- passed6_6Rule( g, snapshots, smear ) &&
- intersectBigWithSS( g, snapshots ) ) ;
-
- }
-
-
- /**
- * Check if a polygon intersects at least one of the snapshots with an area >= 3K.
- *
- * Note: the input is assumed in MAP coordinate.
- *
- * @param g A Geometry (polygon)
- * @param snapshots A list of associated snapshots
- * @return
- */
- private boolean intersectBigWithSS( Geometry g, ArrayList snapshots ) {
-
- boolean addable = false;
-
- if ( snapshots == null || snapshots.size() <= 0 ) {
- addable = true;
- }
- else {
- Coordinate[] pts = PgenUtil.latlonToGrid( g.getCoordinates() );
- Geometry poly = pointsToGeometry( pts );
- for ( Gfa ss : snapshots ) {
- if ( isBiggerInGrid( poly.intersection( gfaToPolygonInGrid( ss ) ) ) ) {
- addable = true;
- break;
- }
- }
- }
-
- return addable;
-
- }
-
- /**
- * Check if a 6-6 polygon intersect snapshot "6" with an area >= 3K.
- *
- * Note: if the smear is not a "6-6" smear or no snapshots, return true.
- * if there are snapshots but no snapshot "6" somehow, return false;
- *
- * @param g A Geometry (polygon)
- * @param snapshots A list of associated snapshots
- * @return
- */
- private boolean passed6_6Rule ( Geometry g, ArrayList snapshots, Gfa smear ) {
-
- boolean addable = true;
-
- if ( snapshots != null && snapshots.size() > 0 &&
- smear.getForecastHours().equals( "6-6" ) ) {
-
- Gfa ss_6 = null;
-
- for ( Gfa ss : snapshots ) {
- if ( ss.getForecastHours().equals( "6" ) ) {
- ss_6 = ss;
- break;
- }
- }
-
- if ( ss_6 == null ) {
- addable = false;
- }
-
- if ( addable ) {
- Coordinate[] pts = PgenUtil.latlonToGrid( g.getCoordinates() );
- Geometry poly = pointsToGeometry( pts );
- if ( !isBiggerInGrid( poly.intersection( gfaToPolygonInGrid( ss_6 ) ) ) ) {
- addable = false;
- }
- }
-
- }
-
- return addable;
-
- }
-
- /**
- * Check if a polygon is bigger than a value in unit of sq. nautical miles.
- * @param g A Geometry
- * @return
- */
- private boolean isBigger( Geometry g, double limit ) {
- return ( g != null && PgenUtil.getSphPolyArea( g ) >= limit );
- }
-
- /**
- * Check if a polygon is bigger than the GFA AREA_LIMIT (3K sq. nautical miles).
- *
- * @param g A Geometry
- * @return
- */
- public boolean isBiggerInMap( Geometry g ) {
-
- return isBigger( g, GfaRules.AREA_LIMIT );
-
- }
-
- /**
- * Check if a polygon is bigger than the GFA AREA_LIMIT (3K sq. nautical miles).
- *
- * Note: the input is assumed to be in grid coordinate, not map coordinate.
- *
- * @param g A Geometry
- * @return
- */
- public boolean isBiggerInGrid( Geometry g ) {
-
- double area = 0.0;
- if ( g instanceof Polygon ) {
- area = PgenUtil.getSphPolyAreaInGrid( (Polygon)g );
- } else if ( g instanceof MultiPolygon ) {
- MultiPolygon mp = (MultiPolygon)g;
- for( int nn = 0; nn < mp.getNumGeometries(); nn++ ) {
- area += PgenUtil.getSphPolyAreaInGrid( (Polygon)mp.getGeometryN( nn ) );
- }
- }
-
- return ( area >= GfaRules.AREA_LIMIT ) ? true : false;
-
- }
-
-
- /**
- * Get a list of states that allows MT_OBSC hazard.
- * @param
- * @return
- */
- public List getMtObscStates(){
-
- if ( mtObscStates == null ) {
- mtObscStates = new ArrayList();
- String xpath = MTOBSC_XPATH;
-
- Document dm = readMtObscTbl();
-
- if ( dm != null ) {
- Node mtObscInfo = dm.selectSingleNode(xpath);
- List nodes = mtObscInfo.selectNodes( "area" );
- for (Node node : nodes) {
- node.selectNodes("state");
- for ( Object nd : node.selectNodes("state") ) {
- mtObscStates.add( ((Node)nd).getText() );
- }
- }
- }
- }
-
- return mtObscStates;
- }
-
- /**
- * Read mt_obsc_states.xml
- * @return - document
- */
- private static Document readMtObscTbl() {
-
- if ( mtObscTbl == null) {
- try {
- String mtObscFile = PgenStaticDataProvider.getProvider().getFileAbsolutePath(
- PgenStaticDataProvider.getProvider().getPgenLocalizationRoot() + "mt_obsc_states.xml");
-
- SAXReader reader = new SAXReader();
- mtObscTbl = reader.read( mtObscFile );
- } catch ( Exception e ) {
- e.printStackTrace();
- }
- }
-
- return mtObscTbl;
- }
-
-
- /**
- * This routine clips a GFA polygon against the international bound and
- * finds the intersection points of the GFA polygon with the bound.
- * Then the intersection points are snapped individually to points
- * outside of the Clipped GFA polygon. Both the intersection points and
- * their snapped matches are returned in map coordinate.
- *
- * The input is assumed to be in MAP coordinate.
- *
- * @param gfaPoly Polygon from GFA
- * @param bndPoly Polygon of international boundary
- * @param clipped Intersection Polygon of international boundary
- * with the GFA polygon (in grid coordinate)
- *
+ * 1. Check if the smear is big enough ( >= 3K sq. nautical miles ) 2. Clip
+ * against the international boundary and check if the intersection is big
+ * enough 3. Clip against FA region boundaries - 3.1 clip the smear polygon
+ * against international boundary. 3.2 clip the result against each FA
+ * region (WEST, CENTRAL, EAST) 3.3 create a list of parts bigger than/equal
+ * to 3K for each region 3.4 hold the parts smaller than 3K for all regions
+ * in another list. 4. Create smears from VALID parts and add into the
+ * return list 4.1 Union the parts in each FA region with the list of small
+ * parts - If a small part touches one of the bigs (only one, I believe), it
+ * will be united into that big and thus be included into a new smear. So no
+ * part gets lost and all will be included eventually. 4.2 Check the
+ * resulting union for VALID geometries and create new smears for each of
+ * them.
+ *
+ * A VALID geometry is one that a. is large enough ( >= 3K sq. nautical
+ * miles ) b. intersects at least one of its associated snapshots (from
+ * which the smear polygon is generated) with an area >= 3K. c. intersects
+ * snapshot "6" with an area >= 3K, if it is a "6-6" smear and there are
+ * snapshots associated with it.
+ *
+ * @param smear
+ * the GFA smear to be clipped
+ * @param snapshots
+ * snapshots from where the input smear is created
+ * @return a list of clipped GFA smears.
*/
- private HashMap getIntlIntersectionPt( Geometry gfaPoly,
- Geometry bndPoly, Geometry clipped )
- {
- HashMap interPts = new HashMap();
-
- //Get points in clockwise order - do not repeat the first point at the end.
- Coordinate[] pts = clipped.getGeometryN( 0 ).getCoordinates();
- Coordinate[] intlPoly = PgenUtil.gridToLatlon( pts );
- ArrayList intlList = new ArrayList();
- intlList.addAll( Arrays.asList( intlPoly ) );
- intlList.remove( intlList.size() - 1 );
- ArrayList cwPoly = GfaSnap.getInstance().reorderInClockwise( intlList , null );
+ public ArrayList clipFARegions(Gfa smear, ArrayList snapshots) {
+
+ ArrayList clippedList = new ArrayList();
+ if (smear.getGfaHazard().equals("FZLVL")) {
+ return clippedList;
+ }
/*
- * Check out each point as either a new point, an international boundary point,
- * or a point of the original polygon.
- */
- int[] ptFlag = new int[ cwPoly.size() ];
- for ( int ii = 0; ii < cwPoly.size(); ii++ ) {
-
- ptFlag[ ii ] = NEW_POINT;
-
- for ( Coordinate bndc : bndPoly.getCoordinates() ) {
- if ( isSamePoint( bndc, cwPoly.get( ii ), SMALLF ) ) {
- ptFlag[ ii ] = BOUND_POINT;
- break;
- }
- }
-
- if ( ptFlag[ ii ] != NEW_POINT ) continue;
-
- for ( Coordinate orgc : gfaPoly.getCoordinates() ) {
- if ( isSamePoint( orgc, cwPoly.get( ii ), SMALLF ) ) {
- ptFlag[ ii ] = ORIGINAL_POINT;
- break;
- }
- }
+ * If the smear size < AREA_LIMIT, stop.
+ */
+ if (!isBiggerInMap(gfaToPolygon(smear))) {
+ return clippedList;
}
- //Shift the array to let it start with an original point.
- int eflag;
- while ( ptFlag[ 0 ] != ORIGINAL_POINT ) {
-
- cwPoly = GfaSnap.getInstance().shiftArray( cwPoly );
-
- eflag = ptFlag[ 0 ];
- for ( int jj = 0; jj < (ptFlag.length - 1); jj++ ) {
- ptFlag[ jj ] = ptFlag[ jj + 1 ];
- }
-
- ptFlag[ ptFlag.length - 1 ] = eflag;
-
- }
-
-
- //Snap and match any new points.
- int snap_indx1;
- int snap_indx2;
- int status;
- int np = cwPoly.size();
- Coordinate[] snapPt = new Coordinate[1];
-
- ArrayList usedPts = new ArrayList();
- usedPts.addAll( cwPoly );
-
- for ( int ii = 0; ii < cwPoly.size(); ii++ ) {
-
- if ( ptFlag[ ii ] != NEW_POINT ) {
- continue;
- }
-
- /*
- * Handle NN case - a "small" triangle may be cut off when the smear intersects
- * the international boundary. The two new close points by the intersection
- * should be replaced by the original point in the smear.
- */
- if ( (ii + 1) < cwPoly.size() ) {
- if ( ptFlag[ ii + 1 ] == NEW_POINT &&
- GfaSnap.getInstance().isCluster( cwPoly.get( ii ), cwPoly.get( ii + 1 ) ) ) {
-
- int ptBefore = (ii - 1 + cwPoly.size() ) % cwPoly.size();
- int ptAfter = (ii + 2 + cwPoly.size() ) % cwPoly.size();
-
- List gfaPts = Arrays.asList( gfaPoly.getCoordinates() );
- int indInGfa1 = findPoint( cwPoly.get( ptBefore ), gfaPts, SMALLF );
- int indInGfa2 = findPoint( cwPoly.get( ptAfter ), gfaPts, SMALLF );
-
- if ( indInGfa1 >= 0 && indInGfa2 >= 0 &&
- (indInGfa2 - indInGfa1) == 2 ) {
-
- int indBtw = indInGfa1 + 1; //the original point between
- if ( GfaSnap.getInstance().isCluster( cwPoly.get( ii ), gfaPts.get( indBtw ) ) &&
- GfaSnap.getInstance().isCluster( cwPoly.get( ii + 1 ), gfaPts.get( indBtw ) ) ) {
+ /*
+ * Do international clipping (in grid coordinate)
+ */
+ boolean _clippingFlg = true;
- interPts.put( cwPoly.get( ii ) , gfaPts.get( indBtw ) );
- interPts.put( cwPoly.get( ii + 1 ) , gfaPts.get( indBtw ) );
+ Geometry intlBound = getFaInternationalBound();
+ Geometry intlBoundInGrid = getFaInternationalBoundInGrid();
- ii++; //skip these two new points.
- continue;
- }
- }
- }
- }
-
- /*
- * All other cases - snap the intersection point to a point with a
- * clustering distance away and outside the polygon. For clustering
- * points, snap them to a single snap point. If fail to snap a cluster
- * of points, try to snap them individually.
- *
- */
- snap_indx1 = ii;
- snap_indx2 = ii;
-
- while ( snap_indx1 > 0 &&
- GfaSnap.getInstance().isCluster( cwPoly.get( ii ),
- cwPoly.get( snap_indx1-1 ) ) ) {
- snap_indx1--;
- }
-
- while ( snap_indx2 < ( np - 1 ) &&
- GfaSnap.getInstance().isCluster( cwPoly.get( ii ),
- cwPoly.get( snap_indx2+1 ) ) ) {
- snap_indx2++;
- }
+ Polygon smearPoly = gfaToPolygon(smear);
+ Polygon smearPolyInGrid = pointsToPolygon(PgenUtil.latlonToGrid(smear
+ .getLinePoints()));
- status = GfaSnap.getInstance().snapPtGFA( snap_indx1, snap_indx2,
- usedPts, null, cwPoly, true, true, 3.0F, snapPt );
-
+ Geometry clipAgstIntlBnd = null;
- if ( status != 0 && ( snap_indx1 != snap_indx2 ) ) {
-
- snap_indx1 = ii;
- snap_indx2 = ii;
-
- status = GfaSnap.getInstance().snapPtGFA( snap_indx1, snap_indx2,
- usedPts, null, cwPoly, true, true, 3.0F, snapPt );
- }
-
- // Snap failed - use the original point.
- if ( status != 0 ) {
- snapPt[ 0 ].x = cwPoly.get( ii ).x;
- snapPt[ 0 ].y = cwPoly.get( ii ).y;
- }
-
- /*
- * Pair the intersection point with its snapped counterpart.
- * Also replace the intersection points with the snapped ones
- * for snapping the next one.
- */
- for ( int jj = snap_indx1; jj <= snap_indx2; jj++ ) {
- interPts.put( cwPoly.get( jj ), snapPt[ 0 ] );
-// cwPoly.set( jj, new Coordinate( snapPt[ 0 ] ) );;
- }
-
- ii += ( snap_indx2 - snap_indx1 ); //added to skip duplicate calculation.
-
- usedPts.add( snapPt[ 0 ] );
-
+ if (smearPolyInGrid.intersects(intlBoundInGrid)) {
+ clipAgstIntlBnd = smearPolyInGrid.intersection(intlBoundInGrid);
}
-
- return interPts;
-
- }
-
- /**
- * Check if two pints are within a given tie distance. If so, they are
- * considered as the same point.
- */
- private boolean isSamePoint( Coordinate p1, Coordinate p2, double tieDist ) {
-
- boolean sameP = false;
- if ( p1 != null && p2 != null &&
- Math.abs( p1.x - p2.x ) < Math.abs( tieDist ) &&
- Math.abs( p1.y - p2.y ) < Math.abs( tieDist ) ) {
- sameP = true;
- }
-
- return sameP;
- }
-
+
+ if (clipAgstIntlBnd == null || clipAgstIntlBnd.getNumGeometries() <= 0
+ || !isBiggerInGrid(clipAgstIntlBnd)) {
+ return clippedList;
+ }
+
+ /*
+ * Find intersection points with the international bound and the common
+ * borders between West and Central, Central and East regions. Then
+ * insert and snap them for later replacement.
+ */
+ HashMap intlPts = getIntlIntersectionPt(
+ smearPoly, intlBound, clipAgstIntlBnd);
+
+ HashMap regionInterPts = getRegionIntersectionPt(smearPoly);
+
+ /*
+ * Build a list of non-reduce-able point, include all bound points and
+ * snapped intersection points
+ */
+ HashMap rgbnds = getFaRegionBounds();
+ HashMap rgbndsInGrid = getFaRegionBoundsInGrid();
+
+ CoordinateList nonReduceable = new CoordinateList();
+ nonReduceable.addAll(intlPts.values(), false);
+ nonReduceable.addAll(regionInterPts.values(), false);
+ for (Geometry gb : rgbnds.values()) {
+ nonReduceable.addAll(Arrays.asList(gb.getCoordinates()), false);
+ }
+
+ // Build a map of all replaceable points
+ HashMap replacePts = new HashMap();
+ replacePts.putAll(intlPts);
+ replacePts.putAll(regionInterPts);
+
+ /*
+ * Regional clipping as following:
+ *
+ * 1. clip the smear polygon against international boundary. 2. clip the
+ * result against each FA region (WEST, CENTRAL, EAST) 3. create a list
+ * of parts bigger than/equal to 3K for each region 4. hold the parts
+ * smaller than 3K for all regions in another list.
+ */
+ HashMap> clipWithRegions = new HashMap>();
+
+ ArrayList smallPoly = new ArrayList();
+
+ for (String regionName : rgbnds.keySet()) {
+
+ clipWithRegions.put(regionName, new ArrayList());
+
+ // Always start from the smear clipped within international bound
+ Geometry startPoly = intlBoundInGrid.intersection(smearPolyInGrid);
+
+ // Do regional clipping as described above.
+ if (_clippingFlg) {
+
+ Geometry regionBnd = rgbndsInGrid.get(regionName);
+
+ if (regionBnd.intersects(startPoly)) {
+ Geometry regionPoly = regionBnd.intersection(startPoly);
+
+ if (regionPoly != null) {
+
+ for (int kk = 0; kk < regionPoly.getNumGeometries(); kk++) {
+ Geometry bigPoly = regionPoly.getGeometryN(kk);
+
+ if (isBiggerInGrid(bigPoly)) {
+ clipWithRegions.get(regionName).add(bigPoly);
+ } else {
+ smallPoly.add(bigPoly);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Create smears from VALID parts and add into the return list
+ *
+ * 1. Union the parts in each FA region with the list of small parts -
+ * If a small part touches one of the bigs (only one, I believe), it
+ * will be united into that big and thus be included into a new smear.
+ * So no part gets lost and all will be included eventually. 2. When a
+ * small part is united into a big one (if they intersect), the common
+ * points are still retained in the result, we need to remove those
+ * extra point. The common points are resulted from the previous
+ * clipping with the regional boundaries. 3. check the resulting union
+ * for VALID geometries and create new smears for each of them.
+ */
+ for (String regionName : clipWithRegions.keySet()) {
+
+ ArrayList bigs = clipWithRegions.get(regionName);
+
+ ArrayList toBeUnioned = new ArrayList();
+ toBeUnioned.addAll(bigs);
+ toBeUnioned.addAll(smallPoly);
+ Geometry result = quickUnion(toBeUnioned);
+
+ ArrayList commPts = getCommonPoints(bigs, smallPoly);
+
+ if (result != null) {
+ for (int kk = 0; kk < result.getNumGeometries(); kk++) {
+
+ Geometry one = result.getGeometryN(kk);
+
+ /*
+ * Remove residue points due to clipping followed by
+ * subsequent union. Done in grid coordinate.
+ */
+ Geometry onePart = removeCommonPoints(one, commPts);
+
+ /*
+ * Clean up some improper cases - done in MAP coordinate.
+ */
+ Coordinate[] gPts = PgenUtil.gridToLatlon(onePart
+ .getCoordinates());
+ ArrayList points = new ArrayList();
+ for (Coordinate c : gPts) {
+ points.add(c);
+ }
+ points.remove(points.size() - 1);
+
+ Geometry cleanPts = cleanupPoints(pointsToGeometry(points));
+
+ if (isAddableAsSmear(cleanPts, snapshots, smear)) {
+
+ // Replace intersection points with their pre-snapped
+ // pair - map
+ Geometry rplPts = replacePts(cleanPts, replacePts);
+
+ // Clean up duplicate point from point replacement - map
+ Geometry finalPts = cleanupPoints(rplPts);
+
+ if (finalPts != null) {
+ // Create a new GFA smear.
+ Gfa newElm = geometryToGfa(smear, finalPts);
+
+ // Mark non-reduce-able points.
+ addReduceFlags(newElm, nonReduceable);
+
+ newElm.addAttribute("FA_REGION", new String(
+ regionName));
+
+ clippedList.add(newElm);
+ }
+ }
+ }
+ }
+
+ }
+
+ return clippedList;
+ }
+
/**
- * This routine clips a GFA polygon against common border between FA regional
- * bounds to find the intersection points of the GFA polygon with the bound.
- * Then the intersection points are snapped individually to points outside
- * of the polygon. Both the intersection points and their snapped matches
- * are returned in map coordinate.
+ * Check if a polygon is valid to be added as a new smear:
+ *
+ * Note: the input geometry is assumed to be in map coordinate.
+ *
+ * 1. it must be large enough ( >= 3K sq. nautical miles ) 2. it must
+ * intersect at least one of its associated snapshots (from which the smear
+ * polygon is generated) with an area >= 3K. 3. if it is "6-6" smear, it
+ * must intersect snapshot "6" with an area >= 3K.
+ *
+ * @param g
+ * A Geometry (polygon) in map coordinate.
+ * @param snapshots
+ * A list of associated snapshots
+ * @param smear
+ * Original GFA smear
+ * @return
+ */
+ public boolean isAddableAsSmear(Geometry g, ArrayList snapshots,
+ Gfa smear) {
+
+ return (isBiggerInMap(g) && passed6_6Rule(g, snapshots, smear) && intersectBigWithSS(
+ g, snapshots));
+
+ }
+
+ /**
+ * Check if a polygon intersects at least one of the snapshots with an area
+ * >= 3K.
+ *
+ * Note: the input is assumed in MAP coordinate.
+ *
+ * @param g
+ * A Geometry (polygon)
+ * @param snapshots
+ * A list of associated snapshots
+ * @return
+ */
+ private boolean intersectBigWithSS(Geometry g, ArrayList snapshots) {
+
+ boolean addable = false;
+
+ if (snapshots == null || snapshots.size() <= 0) {
+ addable = true;
+ } else {
+ Coordinate[] pts = PgenUtil.latlonToGrid(g.getCoordinates());
+ Geometry poly = pointsToGeometry(pts);
+ for (Gfa ss : snapshots) {
+ if (isBiggerInGrid(poly.intersection(gfaToPolygonInGrid(ss)))) {
+ addable = true;
+ break;
+ }
+ }
+ }
+
+ return addable;
+
+ }
+
+ /**
+ * Check if a 6-6 polygon intersect snapshot "6" with an area >= 3K.
+ *
+ * Note: if the smear is not a "6-6" smear or no snapshots, return true. if
+ * there are snapshots but no snapshot "6" somehow, return false;
+ *
+ * @param g
+ * A Geometry (polygon)
+ * @param snapshots
+ * A list of associated snapshots
+ * @return
+ */
+ private boolean passed6_6Rule(Geometry g, ArrayList snapshots,
+ Gfa smear) {
+
+ boolean addable = true;
+
+ if (snapshots != null && snapshots.size() > 0
+ && smear.getForecastHours().equals("6-6")) {
+
+ Gfa ss_6 = null;
+
+ for (Gfa ss : snapshots) {
+ if (ss.getForecastHours().equals("6")) {
+ ss_6 = ss;
+ break;
+ }
+ }
+
+ if (ss_6 == null) {
+ addable = false;
+ }
+
+ if (addable) {
+ Coordinate[] pts = PgenUtil.latlonToGrid(g.getCoordinates());
+ Geometry poly = pointsToGeometry(pts);
+ if (!isBiggerInGrid(poly.intersection(gfaToPolygonInGrid(ss_6)))) {
+ addable = false;
+ }
+ }
+
+ }
+
+ return addable;
+
+ }
+
+ /**
+ * Check if a polygon is bigger than a value in unit of sq. nautical miles.
+ *
+ * @param g
+ * A Geometry
+ * @return
+ */
+ private boolean isBigger(Geometry g, double limit) {
+ return (g != null && PgenUtil.getSphPolyArea(g) >= limit);
+ }
+
+ /**
+ * Check if a polygon is bigger than the GFA AREA_LIMIT (3K sq. nautical
+ * miles).
+ *
+ * @param g
+ * A Geometry
+ * @return
+ */
+ public boolean isBiggerInMap(Geometry g) {
+
+ return isBigger(g, GfaRules.AREA_LIMIT);
+
+ }
+
+ /**
+ * Check if a polygon is bigger than the GFA AREA_LIMIT (3K sq. nautical
+ * miles).
+ *
+ * Note: the input is assumed to be in grid coordinate, not map coordinate.
+ *
+ * @param g
+ * A Geometry
+ * @return
+ */
+ public boolean isBiggerInGrid(Geometry g) {
+
+ double area = 0.0;
+ if (g instanceof Polygon) {
+ area = PgenUtil.getSphPolyAreaInGrid((Polygon) g);
+ } else if (g instanceof MultiPolygon) {
+ MultiPolygon mp = (MultiPolygon) g;
+ for (int nn = 0; nn < mp.getNumGeometries(); nn++) {
+ area += PgenUtil.getSphPolyAreaInGrid((Polygon) mp
+ .getGeometryN(nn));
+ }
+ }
+
+ return (area >= GfaRules.AREA_LIMIT) ? true : false;
+
+ }
+
+ /**
+ * Get a list of states that allows MT_OBSC hazard.
+ *
+ * @param
+ * @return
+ */
+ public List getMtObscStates() {
+
+ if (mtObscStates == null) {
+ mtObscStates = new ArrayList();
+ String xpath = MTOBSC_XPATH;
+
+ Document dm = readMtObscTbl();
+
+ if (dm != null) {
+ Node mtObscInfo = dm.selectSingleNode(xpath);
+ List nodes = mtObscInfo.selectNodes("area");
+ for (Node node : nodes) {
+ node.selectNodes("state");
+ for (Object nd : node.selectNodes("state")) {
+ mtObscStates.add(((Node) nd).getText());
+ }
+ }
+ }
+ }
+
+ return mtObscStates;
+ }
+
+ /**
+ * Read mt_obsc_states.xml
+ *
+ * @return - document
+ */
+ private static Document readMtObscTbl() {
+
+ if (mtObscTbl == null) {
+ try {
+ String mtObscFile = PgenStaticDataProvider.getProvider()
+ .getFileAbsolutePath(
+ PgenStaticDataProvider.getProvider()
+ .getPgenLocalizationRoot()
+ + "mt_obsc_states.xml");
+
+ SAXReader reader = new SAXReader();
+ mtObscTbl = reader.read(mtObscFile);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ return mtObscTbl;
+ }
+
+ /**
+ * This routine clips a GFA polygon against the international bound and
+ * finds the intersection points of the GFA polygon with the bound. Then the
+ * intersection points are snapped individually to points outside of the
+ * Clipped GFA polygon. Both the intersection points and their snapped
+ * matches are returned in map coordinate.
+ *
+ * The input is assumed to be in MAP coordinate.
+ *
+ * @param gfaPoly
+ * Polygon from GFA
+ * @param bndPoly
+ * Polygon of international boundary
+ * @param clipped
+ * Intersection Polygon of international boundary with the GFA
+ * polygon (in grid coordinate)
+ *
+ */
+ private HashMap getIntlIntersectionPt(
+ Geometry gfaPoly, Geometry bndPoly, Geometry clipped) {
+ HashMap interPts = new HashMap();
+
+ // Get points in clockwise order - do not repeat the first point at the
+ // end.
+ Coordinate[] pts = clipped.getGeometryN(0).getCoordinates();
+ Coordinate[] intlPoly = PgenUtil.gridToLatlon(pts);
+ ArrayList intlList = new ArrayList();
+ intlList.addAll(Arrays.asList(intlPoly));
+ intlList.remove(intlList.size() - 1);
+ ArrayList cwPoly = GfaSnap.getInstance()
+ .reorderInClockwise(intlList, null);
+
+ /*
+ * Check out each point as either a new point, an international boundary
+ * point, or a point of the original polygon.
+ */
+ boolean originalFound = false;
+ int[] ptFlag = new int[cwPoly.size()];
+ for (int ii = 0; ii < cwPoly.size(); ii++) {
+
+ ptFlag[ii] = NEW_POINT;
+
+ for (Coordinate bndc : bndPoly.getCoordinates()) {
+ if (isSamePoint(bndc, cwPoly.get(ii), SMALLF)) {
+ ptFlag[ii] = BOUND_POINT;
+ break;
+ }
+ }
+
+ if (ptFlag[ii] != NEW_POINT)
+ continue;
+
+ for (Coordinate orgc : gfaPoly.getCoordinates()) {
+ if (isSamePoint(orgc, cwPoly.get(ii), SMALLF)) {
+ ptFlag[ii] = ORIGINAL_POINT;
+ originalFound = true;
+ break;
+ }
+ }
+ }
+
+ /*
+ * Shift the array to let it start with an original point. Only do this
+ * if at least one original point found.
+ */
+ int eflag;
+ if (originalFound) {
+ while (ptFlag[0] != ORIGINAL_POINT) {
+
+ cwPoly = GfaSnap.getInstance().shiftArray(cwPoly);
+
+ eflag = ptFlag[0];
+ for (int jj = 0; jj < (ptFlag.length - 1); jj++) {
+ ptFlag[jj] = ptFlag[jj + 1];
+ }
+
+ ptFlag[ptFlag.length - 1] = eflag;
+
+ }
+ }
+
+ // Snap and match any new points.
+ int snap_indx1;
+ int snap_indx2;
+ int status;
+ int np = cwPoly.size();
+ Coordinate[] snapPt = new Coordinate[1];
+
+ ArrayList usedPts = new ArrayList();
+ usedPts.addAll(cwPoly);
+
+ for (int ii = 0; ii < cwPoly.size(); ii++) {
+
+ if (ptFlag[ii] != NEW_POINT) {
+ continue;
+ }
+
+ /*
+ * Handle NN case - a "small" triangle may be cut off when the smear
+ * intersects the international boundary. The two new close points
+ * by the intersection should be replaced by the original point in
+ * the smear.
+ */
+ if ((ii + 1) < cwPoly.size()) {
+ if (ptFlag[ii + 1] == NEW_POINT
+ && GfaSnap.getInstance().isCluster(cwPoly.get(ii),
+ cwPoly.get(ii + 1))) {
+
+ int ptBefore = (ii - 1 + cwPoly.size()) % cwPoly.size();
+ int ptAfter = (ii + 2 + cwPoly.size()) % cwPoly.size();
+
+ List gfaPts = Arrays.asList(gfaPoly
+ .getCoordinates());
+ int indInGfa1 = findPoint(cwPoly.get(ptBefore), gfaPts,
+ SMALLF);
+ int indInGfa2 = findPoint(cwPoly.get(ptAfter), gfaPts,
+ SMALLF);
+
+ if (indInGfa1 >= 0 && indInGfa2 >= 0
+ && (indInGfa2 - indInGfa1) == 2) {
+
+ int indBtw = indInGfa1 + 1; // the original point
+ // between
+ if (GfaSnap.getInstance().isCluster(cwPoly.get(ii),
+ gfaPts.get(indBtw))
+ && GfaSnap.getInstance().isCluster(
+ cwPoly.get(ii + 1), gfaPts.get(indBtw))) {
+
+ interPts.put(cwPoly.get(ii), gfaPts.get(indBtw));
+ interPts.put(cwPoly.get(ii + 1), gfaPts.get(indBtw));
+
+ ii++; // skip these two new points.
+ continue;
+ }
+ }
+ }
+ }
+
+ /*
+ * All other cases - snap the intersection point to a point with a
+ * clustering distance away and outside the polygon. For clustering
+ * points, snap them to a single snap point. If fail to snap a
+ * cluster of points, try to snap them individually.
+ */
+ snap_indx1 = ii;
+ snap_indx2 = ii;
+
+ while (snap_indx1 > 0
+ && GfaSnap.getInstance().isCluster(cwPoly.get(ii),
+ cwPoly.get(snap_indx1 - 1))) {
+ snap_indx1--;
+ }
+
+ while (snap_indx2 < (np - 1)
+ && GfaSnap.getInstance().isCluster(cwPoly.get(ii),
+ cwPoly.get(snap_indx2 + 1))) {
+ snap_indx2++;
+ }
+
+ status = GfaSnap.getInstance().snapPtGFA(snap_indx1, snap_indx2,
+ usedPts, null, cwPoly, true, true, 3.0F, snapPt);
+
+ if (status != 0 && (snap_indx1 != snap_indx2)) {
+
+ snap_indx1 = ii;
+ snap_indx2 = ii;
+
+ status = GfaSnap.getInstance().snapPtGFA(snap_indx1,
+ snap_indx2, usedPts, null, cwPoly, true, true, 3.0F,
+ snapPt);
+ }
+
+ // Snap failed - use the original point.
+ if (status != 0) {
+ snapPt[0].x = cwPoly.get(ii).x;
+ snapPt[0].y = cwPoly.get(ii).y;
+ }
+
+ /*
+ * Pair the intersection point with its snapped counterpart. Also
+ * replace the intersection points with the snapped ones for
+ * snapping the next one.
+ */
+ for (int jj = snap_indx1; jj <= snap_indx2; jj++) {
+ interPts.put(cwPoly.get(jj), snapPt[0]);
+ // cwPoly.set( jj, new Coordinate( snapPt[ 0 ] ) );;
+ }
+
+ ii += (snap_indx2 - snap_indx1); // added to skip duplicate
+ // calculation.
+
+ usedPts.add(snapPt[0]);
+
+ }
+
+ return interPts;
+
+ }
+
+ /**
+ * Check if two pints are within a given tie distance. If so, they are
+ * considered as the same point.
+ */
+ private boolean isSamePoint(Coordinate p1, Coordinate p2, double tieDist) {
+
+ boolean sameP = false;
+ if (p1 != null && p2 != null
+ && Math.abs(p1.x - p2.x) < Math.abs(tieDist)
+ && Math.abs(p1.y - p2.y) < Math.abs(tieDist)) {
+ sameP = true;
+ }
+
+ return sameP;
+ }
+
+ /**
+ * This routine clips a GFA polygon against common border between FA
+ * regional bounds to find the intersection points of the GFA polygon with
+ * the bound. Then the intersection points are snapped individually to
+ * points outside of the polygon. Both the intersection points and their
+ * snapped matches are returned in map coordinate.
*
* Note: the input is assumed to be in MAP coordinate
*
- * @param gfaPoly Polygon from GFA
- *
+ * @param gfaPoly
+ * Polygon from GFA
+ *
*/
- private HashMap getRegionIntersectionPt( Geometry gfaPoly )
- {
- HashMap interPtsPair = new HashMap();
+ private HashMap getRegionIntersectionPt(
+ Geometry gfaPoly) {
+ HashMap interPtsPair = new HashMap();
- ArrayList interPts = new ArrayList();
- ArrayList pts;
- ArrayList interIndex = new ArrayList();
- ArrayList indx = new ArrayList();
-
- //Reorder in clockwise - first point is repeated at the end.
- ArrayList gfaPoints = new ArrayList();
- for ( Coordinate c : gfaPoly.getCoordinates() ) {
- gfaPoints.add( c );
- }
-
- ArrayList cwGfaPts = GfaSnap.getInstance().reorderInClockwise( gfaPoints , null );
-
- //Get all intersection point with FA region common border.
- HashMap regionCommBnds = getFaRegionCommBounds();
- for ( Geometry bnd : regionCommBnds.values() ) {
- pts = lineIntersect( cwGfaPts.toArray( new Coordinate[ cwGfaPts.size() ]),
- bnd.getCoordinates(), indx );
-
- if ( pts.size() > 0 ) {
- interPts.addAll( pts );
- interIndex.addAll( indx );
- }
- }
+ ArrayList interPts = new ArrayList();
+ ArrayList pts;
+ ArrayList interIndex = new ArrayList();
+ ArrayList indx = new ArrayList();
- if ( interPts.size() <= 0 ) {
- return interPtsPair;
- }
+ // Reorder in clockwise - first point is repeated at the end.
+ ArrayList gfaPoints = new ArrayList();
+ for (Coordinate c : gfaPoly.getCoordinates()) {
+ gfaPoints.add(c);
+ }
-/*
- ArrayList closePts = new ArrayList();
- ArrayList closeIndex = new ArrayList();
-
- for ( int ii = 0; ii < interPts.size(); ii++ ) {
-
- Coordinate interP = interPts.get( ii );
- Coordinate ptBefore = cwGfaPts.get( interIndex.get( ii ) );
- Coordinate ptAfter = cwGfaPts.get( interIndex.get( ii ) + 1 );
-
- double qdist1 = GfaSnap.getInstance().distance( interP, ptBefore );
- double qdist2 = GfaSnap.getInstance().distance( interP, ptAfter );
-
- double qdist = Math.min( qdist1, qdist2 );
- if ( ( qdist / PgenUtil.NM2M ) <= GfaSnap.CLUSTER_DIST ) {
- closePts.add( interP );
- closeIndex.add( interIndex.get(ii) );
- }
- }
-
- interPts.removeAll( closePts );
- interIndex.removeAll( closeIndex );
-*/
- /*
- * Find the Central boundary points inside the polygon to ensure
- * the new snap points will not cluster with these points.
- */
- ArrayList checkPoints = new ArrayList();
- Geometry centralBnd = getFaRegionBounds().get( "CENTRAL" );
- for ( Coordinate c : centralBnd.getCoordinates() ) {
- Geometry pp = pointsToGeometry( new Coordinate[]{ c } );
- if ( pp.within( gfaPoly ) ) {
- checkPoints.add( c );
- }
- }
+ ArrayList cwGfaPts = GfaSnap.getInstance()
+ .reorderInClockwise(gfaPoints, null);
- /*
- * Now insert each common intersection point into the el polygon
- * and snap it outside of the el polygon.
- *
- * Note: the point cannot be any point of the el polygon or any
- * snap point that has been used.
- *
- * If an intersection point is not within the clustering distance of
- * the point before it (Pb) or point after it (Pa), simply insert it into
- * the polygon and snap it. Otherwise, do the following:
- *
- * 1. Pick the closer one of Pb and Pa as Pn.
- * 2. Check if Pn is within the clustering distance of the common boundary
- * points inside the FROM line.
- * 3. If so, snap Pn to the closest point not within the clustering distance
- * and match the intersection point and Pn to the new point.
- * 4. If not, match the intersection point with Pn.
- */
- ArrayList usedPoints = new ArrayList();
- usedPoints.addAll( cwGfaPts );
-
- ArrayList ePts = new ArrayList();
- for ( int ii = 0; ii < interPts.size(); ii++ ) {
-
- Coordinate interP = interPts.get( ii );
- Coordinate ptBefore = cwGfaPts.get( interIndex.get( ii ) );
- Coordinate ptAfter = cwGfaPts.get( interIndex.get( ii ) + 1 );
-
- double qdist1 = GfaSnap.getInstance().distance( interP, ptBefore );
- double qdist2 = GfaSnap.getInstance().distance( interP, ptAfter );
+ // Get all intersection point with FA region common border.
+ HashMap regionCommBnds = getFaRegionCommBounds();
+ for (Geometry bnd : regionCommBnds.values()) {
+ pts = lineIntersect(
+ cwGfaPts.toArray(new Coordinate[cwGfaPts.size()]),
+ bnd.getCoordinates(), indx);
- int addOne = -1;
- int qmatch = -1;
- double qdist;
+ if (pts.size() > 0) {
+ interPts.addAll(pts);
+ interIndex.addAll(indx);
+ }
+ }
- if ( qdist1 < qdist2 ) {
- qmatch = interIndex.get( ii );
- qdist = qdist1;
- }
- else {
- qmatch = interIndex.get( ii ) + 1;
- qdist = qdist2;
- }
+ if (interPts.size() <= 0) {
+ return interPtsPair;
+ }
- if ( ( qdist / PgenUtil.NM2M ) < GfaSnap.CLUSTER_DIST ) {
+ /*
+ * ArrayList closePts = new ArrayList();
+ * ArrayList closeIndex = new ArrayList();
+ *
+ * for ( int ii = 0; ii < interPts.size(); ii++ ) {
+ *
+ * Coordinate interP = interPts.get( ii ); Coordinate ptBefore =
+ * cwGfaPts.get( interIndex.get( ii ) ); Coordinate ptAfter =
+ * cwGfaPts.get( interIndex.get( ii ) + 1 );
+ *
+ * double qdist1 = GfaSnap.getInstance().distance( interP, ptBefore );
+ * double qdist2 = GfaSnap.getInstance().distance( interP, ptAfter );
+ *
+ * double qdist = Math.min( qdist1, qdist2 ); if ( ( qdist /
+ * PgenUtil.NM2M ) <= GfaSnap.CLUSTER_DIST ) { closePts.add( interP );
+ * closeIndex.add( interIndex.get(ii) ); } }
+ *
+ * interPts.removeAll( closePts ); interIndex.removeAll( closeIndex );
+ */
+ /*
+ * Find the Central boundary points inside the polygon to ensure the new
+ * snap points will not cluster with these points.
+ */
+ ArrayList checkPoints = new ArrayList();
+ Geometry centralBnd = getFaRegionBounds().get("CENTRAL");
+ for (Coordinate c : centralBnd.getCoordinates()) {
+ Geometry pp = pointsToGeometry(new Coordinate[] { c });
+ if (pp.within(gfaPoly)) {
+ checkPoints.add(c);
+ }
+ }
- Coordinate ptMatch = cwGfaPts.get( qmatch );
- for ( Coordinate c : checkPoints ) {
- if ( GfaSnap.getInstance().isCluster( c, ptMatch ) ) {
- addOne = qmatch;
- break;
- }
- }
-
- if ( addOne < 0 ) {
- interPtsPair.put( new Coordinate( interP ),
- new Coordinate( ptMatch ) );
- continue; //Done - ptMatch is a snapped point.
- }
- }
-
- // Snap
- int kk, kk2;
- if ( addOne >= 0 ) {
- kk = addOne;
- kk2 = kk;
+ /*
+ * Now insert each common intersection point into the el polygon and
+ * snap it outside of the el polygon.
+ *
+ * Note: the point cannot be any point of the el polygon or any snap
+ * point that has been used.
+ *
+ * If an intersection point is not within the clustering distance of the
+ * point before it (Pb) or point after it (Pa), simply insert it into
+ * the polygon and snap it. Otherwise, do the following:
+ *
+ * 1. Pick the closer one of Pb and Pa as Pn. 2. Check if Pn is within
+ * the clustering distance of the common boundary points inside the FROM
+ * line. 3. If so, snap Pn to the closest point not within the
+ * clustering distance and match the intersection point and Pn to the
+ * new point. 4. If not, match the intersection point with Pn.
+ */
+ ArrayList usedPoints = new ArrayList();
+ usedPoints.addAll(cwGfaPts);
- ePts.clear();
-// ePts.addAll( cwGfaPts );
- //If the point to be added is at the end, need to insert in - JW - 10/2012.
- if ( addOne == (cwGfaPts.size() - 1) ) {
- ePts.addAll( GfaSnap.getInstance().insertArray( cwGfaPts, interIndex.get( ii ) + 1, interP ) );
- kk = interIndex.get( ii ) + 1;
- kk2 = kk;
- }
- else {
- ePts.addAll( cwGfaPts );
- }
- }
- else {
- ePts.clear();
- ePts.addAll( GfaSnap.getInstance().insertArray( cwGfaPts, interIndex.get( ii ) + 1, interP ) );
- kk = interIndex.get( ii ) + 1;
- kk2 = kk;
- }
+ ArrayList ePts = new ArrayList();
+ for (int ii = 0; ii < interPts.size(); ii++) {
-
- ePts.remove( ePts.size() - 1 );
-
- Coordinate[] snapped = new Coordinate[1];
-
- //snap....
- int status = GfaSnap.getInstance().snapPtGFA( kk, kk2, usedPoints, checkPoints,
- ePts, true, true, 3.0F, snapped );
-
- if ( status != 0 ) {
- if ( addOne >= 0 ) {
- snapped[ 0 ] = new Coordinate( ePts.get( kk ) );
- }
- else {
- snapped[ 0 ] = new Coordinate( interP );
- }
- }
-
- interPtsPair.put( new Coordinate( interP ),
- new Coordinate( snapped[ 0 ] ) );
-
- if ( addOne >= 0 ) {
- interPtsPair.put( new Coordinate( cwGfaPts.get( addOne ) ),
- new Coordinate( snapped[ 0 ] ) );
- }
-
- usedPoints.add( snapped[ 0 ] );
-
- }
+ Coordinate interP = interPts.get(ii);
+ Coordinate ptBefore = cwGfaPts.get(interIndex.get(ii));
+ Coordinate ptAfter = cwGfaPts.get(interIndex.get(ii) + 1);
+ double qdist1 = GfaSnap.getInstance().distance(interP, ptBefore);
+ double qdist2 = GfaSnap.getInstance().distance(interP, ptAfter);
-
- return interPtsPair;
- }
+ int addOne = -1;
+ int qmatch = -1;
+ double qdist;
- /**
- * Computes the intersection points of two multi-point lines.
- *
- * The input is assumed to be in MAP coordinate.
- *
- * Note: 1. the computation is done segment by segment, so if a line is closed
- * it is the caller's responsibility to ensure the first point is repeated
- * at the end of the line.
- * 2. the index of the point before the intersection in line1 is retained. So
- * the index is dependent the direction of the line1 (Clockwise or
- * counterclockwise)
- */
- public ArrayList lineIntersect( Coordinate[] line1, Coordinate[] line2,
- ArrayList indexInLine1 ) {
-
- ArrayList interPts = new ArrayList();
- if ( indexInLine1 == null ) {
- indexInLine1 = new ArrayList();
- }
- else {
- indexInLine1.clear();
- }
+ if (qdist1 < qdist2) {
+ qmatch = interIndex.get(ii);
+ qdist = qdist1;
+ } else {
+ qmatch = interIndex.get(ii) + 1;
+ qdist = qdist2;
+ }
- Coordinate[] line1InGrid = PgenUtil.latlonToGrid( line1 );
- Coordinate[] line2InGrid = PgenUtil.latlonToGrid( line2 );
+ if ((qdist / PgenUtil.NM2M) < GfaSnap.CLUSTER_DIST) {
- Coordinate onePt;
- for ( int ii = 0; ii < (line1.length - 1); ii++ ) {
- for ( int jj = 0; jj < (line2.length - 1); jj++ ) {
-
- onePt = segmentIntersect( line1InGrid[ ii ], line1InGrid[ ii + 1 ],
- line2InGrid[ jj ], line2InGrid[ jj + 1 ] );
- if ( onePt != null ) {
- Coordinate[] onePtInMap = PgenUtil.gridToLatlon( new Coordinate[]{ onePt } );
- interPts.add( new Coordinate( onePtInMap[ 0 ] ) );
- indexInLine1.add( new Integer( ii ) );
- }
- }
- }
-
- return interPts;
- }
-
- /**
- * Computes the intersection points of two line segments (not extended lines).
- *
- * No coordinate system is assumed.
- *
- */
- public Coordinate segmentIntersect( Coordinate seg1_start, Coordinate seg1_end,
- Coordinate seg2_start, Coordinate seg2_end ) {
- Coordinate interPt = null;
-
- Geometry g1 = pointsToLineString( new Coordinate[]{seg1_start, seg1_end} );
- Geometry g2 = pointsToLineString( new Coordinate[]{seg2_start, seg2_end} );
-
- if ( g1.intersects( g2 ) ) {
- interPt = g1.intersection( g2 ).getCoordinates()[0];
- }
-
- return interPt;
- }
+ Coordinate ptMatch = cwGfaPts.get(qmatch);
+ for (Coordinate c : checkPoints) {
+ if (GfaSnap.getInstance().isCluster(c, ptMatch)) {
+ addOne = qmatch;
+ break;
+ }
+ }
- /**
- * Get the common bounds between FA West and Central, Central and East.
- */
- public HashMap getFaRegionCommBounds()
- {
- if ( faRegionCommBounds == null ){
- loadFaRegionCommBounds();
- }
-
- return faRegionCommBounds;
- }
-
- /**
- * Calculate the common bounds between FA West and Central, Central and East.
- */
- private void loadFaRegionCommBounds()
- {
- faRegionCommBounds = new HashMap();
- HashMap rgbnds = getFaRegionBounds();
- ArrayList used = new ArrayList();
-
- for ( String regionName1 : rgbnds.keySet() ) {
- used.add( regionName1 );
- Geometry bnd1 = rgbnds.get( regionName1 );
- for ( String regionName2 : rgbnds.keySet() ) {
- if ( !used.contains( regionName2 ) ) {
- Geometry bnd2 = rgbnds.get( regionName2 );
- if ( bnd1.intersects( bnd2 ) ) {
- String bname = new String( regionName1 + "-" + regionName2 );
- Geometry comm = bnd1.intersection( bnd2 );
- //Use CoordinateList to remove duplicate points
- CoordinateList clist = new CoordinateList( );
- clist.add( comm.getCoordinates(), false );
-
- faRegionCommBounds.put( bname, pointsToLineString( clist.toCoordinateArray() ) );
- }
- }
- }
- }
- }
+ if (addOne < 0) {
+ interPtsPair.put(new Coordinate(interP), new Coordinate(
+ ptMatch));
+ continue; // Done - ptMatch is a snapped point.
+ }
+ }
- /**
- * Get the common bounds between extended SFO/SLC bounds, and
- * extended CHI-BOS and DFW-MIA bounds.
- */
- public HashMap getFaAreaXCommBounds()
- {
- if ( faAreaXCommBounds == null ){
- loadFaAreaXCommBounds();
- }
-
- return faAreaXCommBounds;
- }
-
- /**
- * Calculate the common bounds between extended FA area SLC and dSFC,
- * CHI-BOS and DFW-MIA.
- */
- private void loadFaAreaXCommBounds()
- {
- faAreaXCommBounds = new HashMap();
- HashMap areaxbnds = getFaAreaXBounds();
-
- Geometry bnd1 = areaxbnds.get( "SLC" );
- Geometry bnd2 = areaxbnds.get( "SFO" );
-
- if ( bnd1.intersects( bnd2 ) ) {
- String bname = new String( "SLC-SFO" );
- Geometry comm = bnd1.intersection( bnd2 );
- //Use CoordinateList to remove duplicate points
- CoordinateList clist = new CoordinateList( );
- clist.add( comm.getCoordinates(), false );
-
- faAreaXCommBounds.put( bname, pointsToLineString( clist.toCoordinateArray() ) );
- }
-
- bnd1 = areaxbnds.get( "CHI" );
- bnd2 = areaxbnds.get( "DFW" );
-
- if ( bnd1.intersects( bnd2 ) ) {
- String bname = new String( "CHI-DFW" );
- Geometry comm = bnd1.intersection( bnd2 );
- //Use CoordinateList to remove duplicate points
- CoordinateList clist = new CoordinateList( );
- clist.add( comm.getCoordinates(), false );
-
- faAreaXCommBounds.put( bname, pointsToLineString( clist.toCoordinateArray() ) );
-
- String bname2 = new String( "BOS-MIA" );
- faAreaXCommBounds.put( bname2, pointsToLineString( clist.toCoordinateArray() ) );
-
- }
-
-
- }
+ // Snap
+ int kk, kk2;
+ if (addOne >= 0) {
+ kk = addOne;
+ kk2 = kk;
- /**
- * Cleans up improper point sequence:
- *
- * Note: MAP coordinate is assumed.
- *
- * 1. compresses the consecutive duplicate points to single point
- * 2. compress ABA point sequence to a single point A
- *
- */
- public Geometry cleanupPoints( Geometry geom )
- {
- Coordinate[] gPts = geom.getCoordinates();
- ArrayList gList = new ArrayList();
+ ePts.clear();
+ // ePts.addAll( cwGfaPts );
+ // If the point to be added is at the end, need to insert in -
+ // JW - 10/2012.
+ if (addOne == (cwGfaPts.size() - 1)) {
+ ePts.addAll(GfaSnap.getInstance().insertArray(cwGfaPts,
+ interIndex.get(ii) + 1, interP));
+ kk = interIndex.get(ii) + 1;
+ kk2 = kk;
+ } else {
+ ePts.addAll(cwGfaPts);
+ }
+ } else {
+ ePts.clear();
+ ePts.addAll(GfaSnap.getInstance().insertArray(cwGfaPts,
+ interIndex.get(ii) + 1, interP));
+ kk = interIndex.get(ii) + 1;
+ kk2 = kk;
+ }
- for ( Coordinate c : gPts ) {
- gList.add( new Coordinate( Math.rint(c.x * 100) / 100,
- Math.rint(c.y * 100) / 100 ) );
- }
-
- gList.remove( gList.size() - 1 ); //remove last point.
-
- //Compresses consecutive duplicate points to single point.
+ ePts.remove(ePts.size() - 1);
+
+ Coordinate[] snapped = new Coordinate[1];
+
+ // snap....
+ int status = GfaSnap.getInstance().snapPtGFA(kk, kk2, usedPoints,
+ checkPoints, ePts, true, true, 3.0F, snapped);
+
+ if (status != 0) {
+ if (addOne >= 0) {
+ snapped[0] = new Coordinate(ePts.get(kk));
+ } else {
+ snapped[0] = new Coordinate(interP);
+ }
+ }
+
+ interPtsPair
+ .put(new Coordinate(interP), new Coordinate(snapped[0]));
+
+ if (addOne >= 0) {
+ interPtsPair.put(new Coordinate(cwGfaPts.get(addOne)),
+ new Coordinate(snapped[0]));
+ }
+
+ usedPoints.add(snapped[0]);
+
+ }
+
+ return interPtsPair;
+ }
+
+ /**
+ * Computes the intersection points of two multi-point lines.
+ *
+ * The input is assumed to be in MAP coordinate.
+ *
+ * Note: 1. the computation is done segment by segment, so if a line is
+ * closed it is the caller's responsibility to ensure the first point is
+ * repeated at the end of the line. 2. the index of the point before the
+ * intersection in line1 is retained. So the index is dependent the
+ * direction of the line1 (Clockwise or counterclockwise)
+ */
+ public ArrayList lineIntersect(Coordinate[] line1,
+ Coordinate[] line2, ArrayList indexInLine1) {
+
+ ArrayList interPts = new ArrayList();
+ if (indexInLine1 == null) {
+ indexInLine1 = new ArrayList();
+ } else {
+ indexInLine1.clear();
+ }
+
+ Coordinate[] line1InGrid = PgenUtil.latlonToGrid(line1);
+ Coordinate[] line2InGrid = PgenUtil.latlonToGrid(line2);
+
+ Coordinate onePt;
+ for (int ii = 0; ii < (line1.length - 1); ii++) {
+ for (int jj = 0; jj < (line2.length - 1); jj++) {
+
+ onePt = segmentIntersect(line1InGrid[ii], line1InGrid[ii + 1],
+ line2InGrid[jj], line2InGrid[jj + 1]);
+ if (onePt != null) {
+ Coordinate[] onePtInMap = PgenUtil
+ .gridToLatlon(new Coordinate[] { onePt });
+ interPts.add(new Coordinate(onePtInMap[0]));
+ indexInLine1.add(new Integer(ii));
+ }
+ }
+ }
+
+ return interPts;
+ }
+
+ /**
+ * Computes the intersection points of two line segments (not extended
+ * lines).
+ *
+ * No coordinate system is assumed.
+ *
+ */
+ public Coordinate segmentIntersect(Coordinate seg1_start,
+ Coordinate seg1_end, Coordinate seg2_start, Coordinate seg2_end) {
+ Coordinate interPt = null;
+
+ Geometry g1 = pointsToLineString(new Coordinate[] { seg1_start,
+ seg1_end });
+ Geometry g2 = pointsToLineString(new Coordinate[] { seg2_start,
+ seg2_end });
+
+ if (g1.intersects(g2)) {
+ interPt = g1.intersection(g2).getCoordinates()[0];
+ }
+
+ return interPt;
+ }
+
+ /**
+ * Get the common bounds between FA West and Central, Central and East.
+ */
+ public HashMap getFaRegionCommBounds() {
+ if (faRegionCommBounds == null) {
+ loadFaRegionCommBounds();
+ }
+
+ return faRegionCommBounds;
+ }
+
+ /**
+ * Calculate the common bounds between FA West and Central, Central and
+ * East.
+ */
+ private void loadFaRegionCommBounds() {
+ faRegionCommBounds = new HashMap();
+ HashMap rgbnds = getFaRegionBounds();
+ ArrayList used = new ArrayList();
+
+ for (String regionName1 : rgbnds.keySet()) {
+ used.add(regionName1);
+ Geometry bnd1 = rgbnds.get(regionName1);
+ for (String regionName2 : rgbnds.keySet()) {
+ if (!used.contains(regionName2)) {
+ Geometry bnd2 = rgbnds.get(regionName2);
+ if (bnd1.intersects(bnd2)) {
+ String bname = new String(regionName1 + "-"
+ + regionName2);
+ Geometry comm = bnd1.intersection(bnd2);
+ // Use CoordinateList to remove duplicate points
+ CoordinateList clist = new CoordinateList();
+ clist.add(comm.getCoordinates(), false);
+
+ faRegionCommBounds.put(bname,
+ pointsToLineString(clist.toCoordinateArray()));
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the common bounds between extended SFO/SLC bounds, and extended
+ * CHI-BOS and DFW-MIA bounds.
+ */
+ public HashMap getFaAreaXCommBounds() {
+ if (faAreaXCommBounds == null) {
+ loadFaAreaXCommBounds();
+ }
+
+ return faAreaXCommBounds;
+ }
+
+ /**
+ * Calculate the common bounds between extended FA area SLC and dSFC,
+ * CHI-BOS and DFW-MIA.
+ */
+ private void loadFaAreaXCommBounds() {
+ faAreaXCommBounds = new HashMap();
+ HashMap areaxbnds = getFaAreaXBounds();
+
+ Geometry bnd1 = areaxbnds.get("SLC");
+ Geometry bnd2 = areaxbnds.get("SFO");
+
+ if (bnd1.intersects(bnd2)) {
+ String bname = new String("SLC-SFO");
+ Geometry comm = bnd1.intersection(bnd2);
+ // Use CoordinateList to remove duplicate points
+ CoordinateList clist = new CoordinateList();
+ clist.add(comm.getCoordinates(), false);
+
+ faAreaXCommBounds.put(bname,
+ pointsToLineString(clist.toCoordinateArray()));
+ }
+
+ bnd1 = areaxbnds.get("CHI");
+ bnd2 = areaxbnds.get("DFW");
+
+ if (bnd1.intersects(bnd2)) {
+ String bname = new String("CHI-DFW");
+ Geometry comm = bnd1.intersection(bnd2);
+ // Use CoordinateList to remove duplicate points
+ CoordinateList clist = new CoordinateList();
+ clist.add(comm.getCoordinates(), false);
+
+ faAreaXCommBounds.put(bname,
+ pointsToLineString(clist.toCoordinateArray()));
+
+ String bname2 = new String("BOS-MIA");
+ faAreaXCommBounds.put(bname2,
+ pointsToLineString(clist.toCoordinateArray()));
+
+ }
+
+ }
+
+ /**
+ * Cleans up improper point sequence:
+ *
+ * Note: MAP coordinate is assumed.
+ *
+ * 1. compresses the consecutive duplicate points to single point 2.
+ * compress ABA point sequence to a single point A
+ *
+ */
+ public Geometry cleanupPoints(Geometry geom) {
+ Coordinate[] gPts = geom.getCoordinates();
+ ArrayList gList = new ArrayList();
+
+ for (Coordinate c : gPts) {
+ gList.add(new Coordinate(Math.rint(c.x * 100) / 100, Math
+ .rint(c.y * 100) / 100));
+ }
+
+ gList.remove(gList.size() - 1); // remove last point.
+
+ // Compresses consecutive duplicate points to single point.
int ii = 0, jj, nshift;
- boolean done = false;
- while ( !done ) {
+ boolean done = false;
+ while (!done) {
- jj = ii;
- while ( ( jj + 1 ) < gList.size() &&
- ( Math.abs( gList.get( ii ).x - gList.get( jj + 1 ).x ) < SMALLF &&
- Math.abs( gList.get( ii ).y - gList.get( jj + 1 ).y ) < SMALLF ) ) {
- jj += 1;
- }
- nshift = jj - ii;
+ jj = ii;
+ while ((jj + 1) < gList.size()
+ && (Math.abs(gList.get(ii).x - gList.get(jj + 1).x) < SMALLF && Math
+ .abs(gList.get(ii).y - gList.get(jj + 1).y) < SMALLF)) {
+ jj += 1;
+ }
+ nshift = jj - ii;
- if ( nshift != 0 ) {
- gList = GfaSnap.getInstance().collapseArray( gList, ii, nshift );
- }
+ if (nshift != 0) {
+ gList = GfaSnap.getInstance().collapseArray(gList, ii, nshift);
+ }
- ii++;
+ ii++;
- if ( ii >= gList.size() ) done = true;
+ if (ii >= gList.size())
+ done = true;
}
- //Reduce ABA case to A (line starts at A, goes to B and then back to A).
+ // Reduce ABA case to A (line starts at A, goes to B and then back to
+ // A).
ii = 0;
- done = false;
- while ( !done ) {
+ done = false;
+ while (!done) {
- if ( ( ii + 2) < gList.size() &&
- ( Math.abs( gList.get( ii ).x - gList.get( ii + 2 ).x ) < SMALLF &&
- Math.abs( gList.get( ii ).y - gList.get( ii + 2 ).y ) < SMALLF ) ) {
-
- gList = GfaSnap.getInstance().collapseArray( gList, ii, 2 );
- }
+ if ((ii + 2) < gList.size()
+ && (Math.abs(gList.get(ii).x - gList.get(ii + 2).x) < SMALLF && Math
+ .abs(gList.get(ii).y - gList.get(ii + 2).y) < SMALLF)) {
- ii++;
+ gList = GfaSnap.getInstance().collapseArray(gList, ii, 2);
+ }
+
+ ii++;
+
+ if (ii >= gList.size())
+ done = true;
- if ( ii >= gList.size() ) done = true;
-
}
-
-
- //Make sure the first point is not duplicated at the end.
- if ( ( Math.abs( gList.get( 0 ).x - gList.get( gList.size() - 1 ).x ) < SMALLF &&
- Math.abs( gList.get( 0 ).y - gList.get( gList.size() - 1 ).y ) < SMALLF ) ) {
- gList.remove( gList.size() - 1 );
- }
-
- Coordinate[] outp = gList.toArray( new Coordinate[ gList.size() ] );
-
- return pointsToGeometry( outp ) ;
-
- }
-
- /**
- * Finds and replaces the points in a Geometry with its pair.
- */
- protected Geometry replacePts( Geometry geom, HashMap pairs ) {
-
- Coordinate[] poly = geom.getCoordinates();
- Coordinate[] pts = new Coordinate[ poly.length - 1 ]; //remove last point
-
- for ( int ii = 0; ii < ( poly.length - 1 ); ii++ ) {
-
- pts[ ii ] = new Coordinate( poly[ ii ] );
-
- if ( pairs != null ) {
- for ( Coordinate p : pairs.keySet() ) {
- if ( Math.abs( poly[ ii ].x - p.x ) < SMALLF &&
- Math.abs( poly[ ii ].y - p.y ) < SMALLF ) {
- pts[ ii ] = new Coordinate( pairs.get( p ) );
- break;
- }
- }
- }
- }
-
- return pointsToGeometry( pts );
-
- }
-
- /**
- * Flags a set of points in a GFA element as non-reduce-able they are in tie
- * distance of one of the non-reduce-able points. This strategy helps to
- * preserve the integrity of the boundary after clipping and point reduction.
- *
- * The non-reduce-able points includes: all the FA regional boundary points, the
- * snapped intersection points of the original GFA with the FA international
- * and common regional bounds, as well as the points immediately before and
- * such a non-reduce-able point.
- */
- private void addReduceFlags( Gfa elm, CoordinateList pts ) {
-
- // Note: the first point is not repeated at the end for a DrawableElement.
- Coordinate[] poly = elm.getLinePoints();
- int np = poly.length;
- boolean[] reduceable = new boolean[ np ];
- for ( int ii = 0; ii < np; ii++ ) {
- reduceable[ ii ] = true;
- }
-
- for ( int ii = 0; ii < np; ii++ ) {
- for ( int jj = 0; jj < pts.size(); jj++ ) {
- if ( Math.abs( poly[ ii ].x - pts.getCoordinate( jj ).x ) < SMALLF &&
- Math.abs( poly[ ii ].y - pts.getCoordinate( jj ).y ) < SMALLF ) {
- reduceable[ ii ] = false;
- reduceable[ ( ii - 1 + np ) % np ] = false;
- reduceable[ ( ii + 1 + np ) % np ] = false;
- break;
- }
- }
- }
-
- elm.setReduceFlags( reduceable );
-
- }
-
- /**
- * Finds the common points between two sets of Geometries.
- */
- public ArrayList getCommonPoints( ArrayList big,
- ArrayList small ) {
-
- ArrayList commPts = new ArrayList();
-
- for ( Geometry g1 : big ) {
- for ( Geometry g2 : small ) {
- if ( g1.intersects( g2 ) ) {
- for ( Coordinate c : g1.intersection( g2 ).getCoordinates() ) {
- if ( !commPts.contains( c ) ) {
- commPts.add( new Coordinate( c ) );
- }
- }
- }
- }
- }
-
- return commPts;
- }
-
- /**
- * Remove a list of points from a Geometry and form a new Geometry.
- */
- public Geometry removeCommonPoints( Geometry gm, ArrayList pts ) {
-
- if ( pts == null || pts.size() == 0 ) {
- return gm;
- }
-
- ArrayList newPts = new ArrayList();
-
- Coordinate[] gPts = gm.getCoordinates();
-
- for ( int ii = 0; ii < ( gPts.length - 1 ); ii++ ) {
- if ( !pts.contains( gPts[ ii ] ) ) {
- newPts.add( new Coordinate( gPts[ ii ] ) );
- }
- }
-
- return pointsToGeometry( newPts );
-
- }
-
- /**
- * Load all bounds needed for Gfa processing.
- */
- public void loadGfaBounds() {
-
- //States, FA Regions, FA Areas, Fa extended Areas.
- getStateBounds();
- getFaRegionBounds();
- getFaAreaBounds();
- getFaAreaXBounds();
-
- getGreatLakeBounds();
- getCoastalWaterBounds();
- getFaInternationalBound();
- getMtObscStates();
-
- getFaRegionCommBounds();
- getFaAreaXCommBounds();
-
- //Convert to grid coordinates.
- updateGfaBoundsInGrid();
-
- //Snap points
- SnapVOR.getSnapStns( null, 16 );
-
- }
-
- /**
- * Update all bounds needed for Gfa processing.
- */
- public void updateGfaBoundsInGrid() {
-
- faInternationalBoundInGrid = geometryInGrid( getFaInternationalBound() );
-
- faRegionBoundsInGrid = updateBoundsInGrid( getFaRegionBounds() );
- faAreaBoundsInGrid = updateBoundsInGrid( getFaAreaBounds() );
- faAreaXBoundsInGrid = updateBoundsInGrid( getFaAreaXBounds() );
-
- stateBoundsInGrid = updateBoundsInGrid( getStateBounds() );
-
-/*
- stateBoundsInGrid = updateBoundsInGrid( getStateBounds() );
- StringBuilder invalidSts = new StringBuilder();
- for ( String key : stateBoundsInGrid.keySet() ) {
- Geometry g1 = stateBounds.get( key );
- Geometry g2 = stateBoundsInGrid.get( key );
-
- if ( !g1.isValid() || (g1.isValid() && !g2.isValid()) ) {
- invalidSts.append( " " + key );
- }
- }
-
- System.out.println( "Invalid state = " + invalidSts );
-
- for ( String key : stateBoundsInGrid.keySet() ) {
- Geometry g1 = stateBounds.get( key );
- Geometry g2 = stateBoundsInGrid.get( key );
- System.out.println( "Original State Bound for " + key + " is " + g1.isValid() );
- System.out.println( "\tState Bound in Grid for " + key + " is " + g2.isValid() );
- }
- stateBoundsInGrid = updateBoundsInGrid( getStateBounds() );
-*/
- greatLakesBoundsInGrid = updateBoundsInGrid( getGreatLakeBounds() );
- coastalWaterBoundsInGrid = updateBoundsInGrid( getCoastalWaterBounds() );
- faRegionCommBoundsInGrid = updateBoundsInGrid( getFaRegionCommBounds() );
- faAreaXCommBoundsInGrid = updateBoundsInGrid( getFaAreaXCommBounds() );
-
- }
-
- /**
- * Load all bounds in grid coordinate.
- */
- private HashMap updateBoundsInGrid( HashMap bndInMap ) {
-
- HashMap bndInGrid = new HashMap