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:
parent
6531df3ab0
commit
3cdabeb7cd
2 changed files with 137 additions and 96 deletions
|
@ -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) {
|
||||||
|
|
|
@ -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 };
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue