Issue #1170 Changed WorldWrapCorrector to take into account interior rings in polygons and world wrap correct them as well.

Change-Id: I4756cffb310ba9c7eab707b3764f667db01f886f

Former-commit-id: 932e4da3a9 [formerly ac26e6f39bc21ecb4b2bd13a20f72d4f4c470e67]
Former-commit-id: 5a9d7eea35
This commit is contained in:
Max Schenkelberg 2012-09-13 14:37:20 -05:00
parent 6531df3ab0
commit 3cdabeb7cd
2 changed files with 137 additions and 96 deletions

View file

@ -177,7 +177,7 @@ public class GLShadedShapeBase implements IShape {
for (FloatBuffer[] contours : polygons) { for (FloatBuffer[] contours : polygons) {
glu.gluTessProperty(tessellator, GLU.GLU_TESS_WINDING_RULE, glu.gluTessProperty(tessellator, GLU.GLU_TESS_WINDING_RULE,
GLU.GLU_TESS_WINDING_NONZERO); GLU.GLU_TESS_WINDING_ODD);
glu.gluTessBeginPolygon(tessellator, (double[]) null); glu.gluTessBeginPolygon(tessellator, (double[]) null);
int polygonStart = vertexBuffer.position() / 2; int polygonStart = vertexBuffer.position() / 2;
for (FloatBuffer contour : contours) { for (FloatBuffer contour : contours) {

View file

@ -20,7 +20,6 @@
package com.raytheon.uf.common.geospatial.util; package com.raytheon.uf.common.geospatial.util;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import org.geotools.coverage.grid.GeneralGridGeometry; import org.geotools.coverage.grid.GeneralGridGeometry;
@ -29,6 +28,7 @@ import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection; import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.Polygon; import com.vividsolutions.jts.geom.Polygon;
/** /**
@ -108,111 +108,152 @@ public class WorldWrapCorrector {
// we split it up into sections by intersecting with a 360 deg // we split it up into sections by intersecting with a 360 deg
// inverse central meridian. We then normalize the points for each // inverse central meridian. We then normalize the points for each
// section back to -180 to 180 // section back to -180 to 180
boolean handle = false;
if (checker.needsChecking()) { if (checker.needsChecking()) {
boolean polygon = g instanceof Polygon; List<Geometry> geoms = new ArrayList<Geometry>();
Coordinate[] coords = g.getCoordinates(); if (g instanceof Polygon) {
if (polygon) {
// remove duplicate last point for polygon
coords = Arrays.copyOf(coords, coords.length - 1);
}
int length = coords.length + (polygon ? 0 : -1);
int truLen = coords.length;
double currOffset = 0.0;
double minOffset = 0.0, maxOffset = 0.0;
for (int i = 0; i < length; ++i) {
int ip1 = (i + 1) % truLen;
Coordinate a = coords[i];
Coordinate b = coords[ip1];
if (ip1 != 0) {
b.x += currOffset;
}
Boolean low = null;
if (a.x - b.x > 180.0) {
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;
b.x -= 360.0;
if (currOffset < minOffset) {
minOffset = currOffset;
}
} else {
currOffset += 360;
b.x += 360;
if (currOffset > maxOffset) {
maxOffset = currOffset;
}
}
}
}
if (handle) {
// All coords in geometry should be denormalized now, get
// adjusted envelope, divide envelope into sections, for
// each section, intersect with geometry and add to
// geom list
List<Geometry> sections = new ArrayList<Geometry>();
List<Double> rolls = new ArrayList<Double>();
GeometryFactory gf = g.getFactory(); GeometryFactory gf = g.getFactory();
double delta = 0.00001; Polygon p = (Polygon) g;
double start = inverseCentralMeridian + minOffset - 360; LineString extRing = p.getExteriorRing();
double end = inverseCentralMeridian + maxOffset + 360; Polygon extPolygon = gf
double minY = -90, maxY = 90; .createPolygon(gf.createLinearRing(extRing
while (start < end) { .getCoordinates()), null);
double useStart = start; double[] offsets = flattenGeometry(extPolygon);
double useEnd = start + 360; List<Geometry> extRings = new ArrayList<Geometry>();
double minX = useStart + delta; correct(extRings, extPolygon, inverseCentralMeridian,
double maxX = (useEnd) - delta; offsets[0], offsets[1]);
List<Geometry> intRings = new ArrayList<Geometry>(
Geometry section = gf.createPolygon( p.getNumInteriorRing());
gf.createLinearRing(new Coordinate[] { for (int n = 0; n < p.getNumInteriorRing(); ++n) {
new Coordinate(minX, maxY), Polygon intRing = gf.createPolygon(gf
new Coordinate(maxX, maxY), .createLinearRing(p.getInteriorRingN(n)
new Coordinate(maxX, minY), .getCoordinates()), null);
new Coordinate(minX, minY), offsets = flattenGeometry(intRing);
new Coordinate(minX, maxY) }), null); correct(intRings, intRing, inverseCentralMeridian,
section = section.intersection(g); offsets[0], offsets[1]);
if (section.isEmpty() == false) { }
sections.add(section); for (Geometry ext : extRings) {
rolls.add(useEnd); 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);
} }
start += 360;
}
// We need to roll the geometries back into the -180 to 180
// range. That is why we kept track of the meridian used
for (int i = 0; i < sections.size(); ++i) {
Geometry section = sections.get(i);
double rollVal = rolls.get(i);
rollLongitudes(section, rollVal, inverseCentralMeridian);
geomList.add(section);
} }
} else {
double[] offsets = flattenGeometry(g);
double minOffset = offsets[0];
double maxOffset = offsets[1];
correct(geoms, g, inverseCentralMeridian, minOffset,
maxOffset);
} }
} for (Geometry geom : geoms) {
rollGeometry(geom);
if (!handle) { }
geomList.addAll(geoms);
} else {
geomList.add(g); geomList.add(g);
} }
} }
} }
private void rollLongitudes(Geometry g, double inverseCentralMeridianUsed, private void correct(List<Geometry> geoms, Geometry flattenedGeom,
double inverseCentralMeridian) { double inverseCentralMeridian, double minOffset, double maxOffset) {
double diff = inverseCentralMeridian - inverseCentralMeridianUsed; GeometryFactory gf = flattenedGeom.getFactory();
if (diff != 0) { double delta = 0.00001;
for (Coordinate c : g.getCoordinates()) { double start = inverseCentralMeridian + minOffset - 360;
c.x += diff; double end = inverseCentralMeridian + maxOffset + 360;
double minY = -90, maxY = 90;
while (start < end) {
double useStart = start;
double useEnd = start + 360;
double minX = useStart + delta;
double maxX = (useEnd) - delta;
Geometry section = gf.createPolygon(
gf.createLinearRing(new Coordinate[] {
new Coordinate(minX, maxY),
new Coordinate(maxX, maxY),
new Coordinate(maxX, minY),
new Coordinate(minX, minY),
new Coordinate(minX, maxY) }), null);
section = section.intersection(flattenedGeom);
if (section.isEmpty() == false) {
geoms.add(section);
}
start += 360.0;
}
}
/**
* Inverse of {@link #flattenGeometry(Geometry)}, converts geometry
* coordinates so they are between -180/180
*
* @param geom
*/
private void rollGeometry(Geometry geom) {
for (Coordinate c : geom.getCoordinates()) {
while (c.x <= -180.0) {
c.x += 360.0;
}
while (c.x > 180.0) {
c.x -= 360.0;
} }
} }
} }
/**
* Flattens a geometries coordinates so they are continuous if they wrap
* over the 180/-180 line. Returns the min/max offset used for flattening.
* min/max offset of 0.0 means no flattening occurred, all points were
* continuous between -180/180
*
* @param geom
*/
private double[] flattenGeometry(Geometry geom) {
double currOffset = 0.0;
double minOffset = 0.0, maxOffset = 0.0;
Coordinate[] coords = geom.getCoordinates();
int length = coords.length;
for (int i = 0; i < length; ++i) {
int ip1 = (i + 1) % length;
Coordinate a = coords[i];
Coordinate b = coords[ip1];
if (ip1 != 0) {
b.x += currOffset;
}
Boolean low = null;
if (a.x - b.x > 180.0) {
low = false;
} else if (b.x - a.x > 180.0) {
low = true;
}
if (low != null) {
// we wrap either low end or high
if (low) {
currOffset -= 360;
b.x -= 360.0;
if (currOffset < minOffset) {
minOffset = currOffset;
}
} else {
currOffset += 360;
b.x += 360;
if (currOffset > maxOffset) {
maxOffset = currOffset;
}
}
}
}
return new double[] { minOffset, maxOffset };
}
} }