Issue #1170 Fixed slow down caused by differencing geometries too much. Switched to reconstructing polygon with holes instead of differencing to readd holes. Fixed world wrap checker to ensure points are between a 360 normalized range based on inverse central meridian.
Change-Id: Icfffddeeaa9feff8d9b996ce0021acb93d216bab Former-commit-id:5a7ed945e2
[formerly9d5a7cc39c
] [formerly291871bbad
[formerly 7ca7360f261e199c813f3a48d7d0faabada61dba]] Former-commit-id:291871bbad
Former-commit-id:b300507bbc
This commit is contained in:
parent
9965ae1281
commit
b260613a4c
3 changed files with 141 additions and 75 deletions
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<Geometry> geomList,
|
||||
double inverseCentralMeridian) {
|
||||
private void wrapCorrect(Geometry g, List<Geometry> 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<Geometry> geoms = new ArrayList<Geometry>();
|
||||
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<Geometry> extRings = new ArrayList<Geometry>();
|
||||
correct(extRings, extPolygon, inverseCentralMeridian,
|
||||
offsets[0], offsets[1]);
|
||||
List<Geometry> intRings = new ArrayList<Geometry>(
|
||||
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<Geometry> geoms = new ArrayList<Geometry>();
|
||||
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<Geometry> extRings = new ArrayList<Geometry>();
|
||||
correct(extRings, extPolygon, offsets);
|
||||
List<Polygon> polygons = new ArrayList<Polygon>();
|
||||
for (Geometry geom : extRings) {
|
||||
extractPolygons(polygons, geom);
|
||||
}
|
||||
|
||||
// World wrap correct each interior ring
|
||||
List<Geometry> intRings = new ArrayList<Geometry>(
|
||||
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<Polygon> interiorPolygons = new LinkedList<Polygon>();
|
||||
for (Geometry geom : intRings) {
|
||||
extractPolygons(interiorPolygons, geom);
|
||||
}
|
||||
List<PreparedGeometry> preparedInteriorPolygons = new LinkedList<PreparedGeometry>();
|
||||
for (Polygon intPoly : interiorPolygons) {
|
||||
preparedInteriorPolygons.add(PreparedGeometryFactory
|
||||
.prepare(intPoly));
|
||||
}
|
||||
|
||||
// Final polygon list (may create multipolygon out of)
|
||||
List<Polygon> finalPolys = new ArrayList<Polygon>(
|
||||
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<LinearRing> interiorRings = new ArrayList<LinearRing>();
|
||||
Iterator<PreparedGeometry> 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<Geometry> 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<Polygon> 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue