diff --git a/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/AbstractGLMesh.java b/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/AbstractGLMesh.java index 7b245cb6a1..0168403fe3 100644 --- a/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/AbstractGLMesh.java +++ b/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/AbstractGLMesh.java @@ -356,16 +356,16 @@ public abstract class AbstractGLMesh implements IMesh { // Get various x distances to use as weights in interpolating double abDist = 360 - Math.abs(ax - bx); double acDist = 360 - Math.abs(ax - cx); - double amDist = 360 + ax - wwc.getInverseCentralMeridian(); + double amDist = ax - wwc.getLowInverseCentralMeridian(); if (amDist > 360) { amDist = amDist - 360; } // x location to use for midpoints on the triangle side, should be on // same side of central meridian as a - double tx = wwc.getInverseCentralMeridian() - 360 + 0.00001; + double tx = wwc.getLowInverseCentralMeridian() + 0.00001; // x location to use for midpoints on the quad side, should be on // same side of central meridian as b and c - double qx = wwc.getInverseCentralMeridian() - 0.00001; + double qx = wwc.getHighInverseCentralMeridian() - 0.00001; // If a is closer to the central meridian on the other side then switch // amDist, tx, and qx if (amDist > 180) { diff --git a/edexOsgi/com.raytheon.uf.common.geospatial/src/com/raytheon/uf/common/geospatial/util/WorldWrapChecker.java b/edexOsgi/com.raytheon.uf.common.geospatial/src/com/raytheon/uf/common/geospatial/util/WorldWrapChecker.java index 203fe09a48..a0b1fe4d55 100644 --- a/edexOsgi/com.raytheon.uf.common.geospatial/src/com/raytheon/uf/common/geospatial/util/WorldWrapChecker.java +++ b/edexOsgi/com.raytheon.uf.common.geospatial/src/com/raytheon/uf/common/geospatial/util/WorldWrapChecker.java @@ -51,9 +51,9 @@ import com.raytheon.uf.common.status.UFStatus.Priority; public class WorldWrapChecker { - private double inverseCentralMeridian = Double.NaN; + private double lowInverseCentralMeridian = Double.NaN; - private boolean low = false; + private double highInverseCentralMeridian = Double.NaN; private boolean checkForWrapping = false; @@ -67,16 +67,14 @@ public class WorldWrapChecker { AbstractProvider.CENTRAL_MERIDIAN.getName().getCode()) .doubleValue(); } - inverseCentralMeridian = centralMeridian + 180.0; - if (inverseCentralMeridian > 180.0) { - inverseCentralMeridian -= 360.0; - low = true; - } - double l1 = inverseCentralMeridian - .1; - double l2 = inverseCentralMeridian - .2; - double r1 = inverseCentralMeridian - 359.9; - double r2 = inverseCentralMeridian - 359.8; + highInverseCentralMeridian = centralMeridian + 180.0; + lowInverseCentralMeridian = centralMeridian - 180.0; + + double l1 = highInverseCentralMeridian - .1; + double l2 = highInverseCentralMeridian - .2; + double r1 = highInverseCentralMeridian - 359.9; + double r2 = highInverseCentralMeridian - 359.8; try { MathTransform latLonToGrid = new DefaultMathTransformFactory() @@ -121,15 +119,26 @@ public class WorldWrapChecker { return Math.abs(aLon - bLon) > 180.0; } - public double getInverseCentralMeridian() { - return inverseCentralMeridian; + /** + * @return the lowInverseCentralMeridian + */ + public double getLowInverseCentralMeridian() { + return lowInverseCentralMeridian; + } + + /** + * @return the highInverseCentralMeridian + */ + public double getHighInverseCentralMeridian() { + return highInverseCentralMeridian; } public double toProjectionRange(double aLon) { - if (low && aLon < inverseCentralMeridian) { - aLon += 360; - } else if (!low && aLon > inverseCentralMeridian) { - aLon -= 360; + while (aLon < lowInverseCentralMeridian) { + aLon += 360.0; + } + while (aLon > highInverseCentralMeridian) { + aLon -= 360.0; } return aLon; } diff --git a/edexOsgi/com.raytheon.uf.common.geospatial/src/com/raytheon/uf/common/geospatial/util/WorldWrapCorrector.java b/edexOsgi/com.raytheon.uf.common.geospatial/src/com/raytheon/uf/common/geospatial/util/WorldWrapCorrector.java index 9771d4c39b..866b9f10ef 100644 --- a/edexOsgi/com.raytheon.uf.common.geospatial/src/com/raytheon/uf/common/geospatial/util/WorldWrapCorrector.java +++ b/edexOsgi/com.raytheon.uf.common.geospatial/src/com/raytheon/uf/common/geospatial/util/WorldWrapCorrector.java @@ -20,6 +20,8 @@ package com.raytheon.uf.common.geospatial.util; import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import org.geotools.coverage.grid.GeneralGridGeometry; @@ -29,7 +31,10 @@ import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryCollection; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.LineString; +import com.vividsolutions.jts.geom.LinearRing; import com.vividsolutions.jts.geom.Polygon; +import com.vividsolutions.jts.geom.prep.PreparedGeometry; +import com.vividsolutions.jts.geom.prep.PreparedGeometryFactory; /** * This class uses the WorldWrapChecker to correct wrapping of geometries that @@ -76,7 +81,7 @@ public class WorldWrapCorrector { if (checker.needsChecking() == false) { geoms.add(geom); } else { - wrapCorrect(geom, geoms, checker.getInverseCentralMeridian()); + wrapCorrect(geom, geoms); } return geom.getFactory().createGeometryCollection( geoms.toArray(new Geometry[geoms.size()])); @@ -90,11 +95,10 @@ public class WorldWrapCorrector { * @param g * @param geomList */ - private void wrapCorrect(Geometry g, List geomList, - double inverseCentralMeridian) { + private void wrapCorrect(Geometry g, List geomList) { if (g instanceof GeometryCollection) { for (int n = 0; n < g.getNumGeometries(); ++n) { - wrapCorrect(g.getGeometryN(n), geomList, inverseCentralMeridian); + wrapCorrect(g.getGeometryN(n), geomList); } } else if (g.isEmpty() == false) { // Algorithm: @@ -108,71 +112,110 @@ public class WorldWrapCorrector { // we split it up into sections by intersecting with a 360 deg // inverse central meridian. We then normalize the points for each // section back to -180 to 180 - if (checker.needsChecking()) { - List geoms = new ArrayList(); - if (g instanceof Polygon) { - GeometryFactory gf = g.getFactory(); - Polygon p = (Polygon) g; - LineString extRing = p.getExteriorRing(); - Polygon extPolygon = gf - .createPolygon(gf.createLinearRing(extRing - .getCoordinates()), null); - double[] offsets = flattenGeometry(extPolygon); - List extRings = new ArrayList(); - correct(extRings, extPolygon, inverseCentralMeridian, - offsets[0], offsets[1]); - List intRings = new ArrayList( - p.getNumInteriorRing()); - for (int n = 0; n < p.getNumInteriorRing(); ++n) { - Polygon intRing = gf.createPolygon(gf - .createLinearRing(p.getInteriorRingN(n) - .getCoordinates()), null); - offsets = flattenGeometry(intRing); - correct(intRings, intRing, inverseCentralMeridian, - offsets[0], offsets[1]); - } - for (Geometry ext : extRings) { - for (int n1 = 0; n1 < ext.getNumGeometries(); ++n1) { - Geometry geom = ext.getGeometryN(n1); - for (Geometry intRing : intRings) { - for (int n2 = 0; n2 < intRing - .getNumGeometries(); ++n2) { - geom = geom.difference(intRing - .getGeometryN(n2)); - } - } - geoms.add(geom); + List geoms = new ArrayList(); + if (g instanceof Polygon) { + GeometryFactory gf = g.getFactory(); + Polygon p = (Polygon) g; + LineString extRing = p.getExteriorRing(); + Polygon extPolygon = gf.createPolygon( + gf.createLinearRing(extRing.getCoordinates()), null); + // World wrap correct exterior ring and extract polygons + double[] offsets = flattenGeometry(extPolygon); + List extRings = new ArrayList(); + correct(extRings, extPolygon, offsets); + List polygons = new ArrayList(); + for (Geometry geom : extRings) { + extractPolygons(polygons, geom); + } + + // World wrap correct each interior ring + List intRings = new ArrayList( + p.getNumInteriorRing()); + for (int n = 0; n < p.getNumInteriorRing(); ++n) { + Polygon intRing = gf.createPolygon(gf.createLinearRing(p + .getInteriorRingN(n).getCoordinates()), null); + offsets = flattenGeometry(intRing); + correct(intRings, intRing, offsets); + } + + // Extract polygons and "preprare" them for intersections + List interiorPolygons = new LinkedList(); + for (Geometry geom : intRings) { + extractPolygons(interiorPolygons, geom); + } + List preparedInteriorPolygons = new LinkedList(); + for (Polygon intPoly : interiorPolygons) { + preparedInteriorPolygons.add(PreparedGeometryFactory + .prepare(intPoly)); + } + + // Final polygon list (may create multipolygon out of) + List finalPolys = new ArrayList( + polygons.size()); + for (Polygon polygon : polygons) { + // For each polygon, check if it intersects any interior + // polygons. If so, add them to interior ring list so we + // can reconstruct with them in place + List interiorRings = new ArrayList(); + Iterator preparedIntPolys = preparedInteriorPolygons + .iterator(); + while (preparedIntPolys.hasNext()) { + PreparedGeometry prepIntPoly = preparedIntPolys.next(); + boolean intersects = prepIntPoly.intersects(polygon); + if (intersects) { + preparedIntPolys.remove(); + interiorRings.add(gf + .createLinearRing(((Polygon) prepIntPoly + .getGeometry()).getExteriorRing() + .getCoordinates())); } } + + if (interiorRings.size() > 0) { + // add holes to polygon + polygon = gf.createPolygon(gf.createLinearRing(polygon + .getExteriorRing().getCoordinates()), + interiorRings.toArray(new LinearRing[0])); + } + finalPolys.add(polygon); + } + + if (finalPolys.size() > 1) { + // More than one polygon resulting, create MultiPolygon + geoms.add(gf.createMultiPolygon(finalPolys + .toArray(new Polygon[0]))); } else { - double[] offsets = flattenGeometry(g); - double minOffset = offsets[0]; - double maxOffset = offsets[1]; - correct(geoms, g, inverseCentralMeridian, minOffset, - maxOffset); + // 1 or 0 polygons, just add to list + for (Polygon polygon : finalPolys) { + geoms.add(polygon); + } } - for (Geometry geom : geoms) { - rollGeometry(geom); - } - geomList.addAll(geoms); } else { - geomList.add(g); + double[] offsets = flattenGeometry(g); + correct(geoms, g, offsets); } + for (Geometry geom : geoms) { + rollGeometry(geom); + } + geomList.addAll(geoms); + } else { + geomList.add(g); } } private void correct(List geoms, Geometry flattenedGeom, - double inverseCentralMeridian, double minOffset, double maxOffset) { - if (minOffset == 0.0 && maxOffset == 0.0) { + double[] offsets) { + if (offsets == null) { // no offsets to apply, add and return geoms.add(flattenedGeom); return; } else if (flattenedGeom.isValid()) { - // Only apply world wrap correcting to valid geometries + // Only apply world wrap correcting to valid geometries, otherwise + // throw them out since we can't guarantee integrity GeometryFactory gf = flattenedGeom.getFactory(); double delta = 0.00001; - double start = inverseCentralMeridian + minOffset - 360; - double end = inverseCentralMeridian + maxOffset + 360; + double start = checker.getLowInverseCentralMeridian() + offsets[0]; + double end = checker.getHighInverseCentralMeridian() + offsets[1]; double minY = -90, maxY = 90; while (start < end) { @@ -205,7 +248,7 @@ public class WorldWrapCorrector { */ private void rollGeometry(Geometry geom) { for (Coordinate c : geom.getCoordinates()) { - while (c.x <= -180.0) { + while (c.x < -180.0) { c.x += 360.0; } while (c.x > 180.0) { @@ -221,8 +264,10 @@ public class WorldWrapCorrector { * continuous between -180/180 * * @param geom + * @return null if geometry does not need to be corrected */ private double[] flattenGeometry(Geometry geom) { + boolean handle = false; double currOffset = 0.0; double minOffset = 0.0, maxOffset = 0.0; Coordinate[] coords = geom.getCoordinates(); @@ -241,9 +286,12 @@ public class WorldWrapCorrector { low = false; } else if (b.x - a.x > 180.0) { low = true; + } else if (checker.check(a.x, b.x)) { + handle = true; } if (low != null) { + handle = true; // we wrap either low end or high if (low) { currOffset -= 360; @@ -260,7 +308,16 @@ public class WorldWrapCorrector { } } } - return new double[] { minOffset, maxOffset }; + return handle ? new double[] { minOffset, maxOffset } : null; } + private static void extractPolygons(List polygons, Geometry geom) { + if (geom instanceof Polygon) { + polygons.add((Polygon) geom); + } else if (geom instanceof GeometryCollection) { + for (int n = 0; n < geom.getNumGeometries(); ++n) { + extractPolygons(polygons, geom.getGeometryN(n)); + } + } + } }