Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix/3.4 renderer clipping and crash fix #494

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Occam Labs UG (haftungsbeschränkt)
----------------------------------------------------------------------------*/
package org.deegree.rendering.r2d;

import static org.deegree.commons.utils.math.MathUtils.isZero;
import static org.deegree.rendering.r2d.OrientationFixer.fixOrientation;

import org.deegree.geometry.Envelope;
Expand All @@ -51,6 +52,9 @@ Occam Labs UG (haftungsbeschränkt)
import org.deegree.geometry.standard.AbstractDefaultGeometry;
import org.deegree.geometry.standard.DefaultEnvelope;
import org.deegree.geometry.standard.primitive.DefaultPoint;
import org.deegree.style.styling.LineStyling;
import org.deegree.style.styling.PolygonStyling;
import org.deegree.style.styling.components.Stroke;

/**
* Responsible for clipping geometries to the area of the viewport.
Expand Down Expand Up @@ -99,6 +103,10 @@ Geometry clipGeometry( final Geometry geom ) {
if ( jtsOrig == jtsClipped ) {
return geom;
}
if ( isInvertedOrientation( jtsOrig ) ) {
return clippedGeometry;
}

return fixOrientation( clippedGeometry, clippedGeometry.getCoordinateSystem() );
} catch ( UnsupportedOperationException e ) {
// use original geometry if intersection not supported by JTS
Expand All @@ -107,5 +115,66 @@ Geometry clipGeometry( final Geometry geom ) {
}
return geom;
}

/**
* Check if the passed Geometry is a Polygon (or the first Geometry of a Collection) and the exterior Ring has CW orientation
*
* This helper is only a workaround to render polygons in CW/CCW order in the same manner clipped as unclipped
*
* TODO This should be resolved in a more universal way, see https://github.com/deegree/deegree3/issues/645
*
* @param jtsGeom JTS Geometry to be evaluated
* @return boolean true if (first) Geometry is Polygon with CW external ring, false otherwise
*/
private boolean isInvertedOrientation( com.vividsolutions.jts.geom.Geometry jtsGeom ) {
com.vividsolutions.jts.geom.Polygon poly = null;
try {
if ( jtsGeom instanceof com.vividsolutions.jts.geom.GeometryCollection && //
( (com.vividsolutions.jts.geom.GeometryCollection) jtsGeom ).getNumGeometries() > 0 ) {
com.vividsolutions.jts.geom.Geometry firstGeom;
firstGeom = ( (com.vividsolutions.jts.geom.GeometryCollection) jtsGeom ).getGeometryN( 0 );
if ( firstGeom instanceof com.vividsolutions.jts.geom.Polygon ) {
poly = (com.vividsolutions.jts.geom.Polygon) firstGeom;
}
} else if ( jtsGeom instanceof com.vividsolutions.jts.geom.Polygon ) {
poly = (com.vividsolutions.jts.geom.Polygon) jtsGeom;
}

//TRICKY check if polygon exterior is CW
if ( poly != null ) {
com.vividsolutions.jts.geom.Coordinate[] coords = poly.getExteriorRing().getCoordinates();
if ( !com.vividsolutions.jts.algorithm.CGAlgorithms.isCCW( coords ) ) {
return true;
}
}
} catch ( Exception ign ) {
// treat as not affected
}

return false;
}

public static boolean isGenerationExpensive( PolygonStyling styling ) {
if ( styling == null )
return false;

return ( !isZero( styling.perpendicularOffset ) || isGenerationExpensive( styling.stroke ));
}

public static boolean isGenerationExpensive( LineStyling styling ) {
if ( styling == null )
return false;

return ( !isZero( styling.perpendicularOffset ) || isGenerationExpensive( styling.stroke ) );
}

private static boolean isGenerationExpensive( Stroke styling ) {
if ( styling == null )
return false;

if ( styling.dasharray != null || styling.stroke != null )
return true;

return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
package org.deegree.rendering.r2d;

import static org.deegree.geometry.utils.GeometryUtils.envelopeToPolygon;
import static org.deegree.rendering.r2d.GeometryClipper.isGenerationExpensive;
import static org.deegree.rendering.r2d.RenderHelper.calculateResolution;
import static org.slf4j.LoggerFactory.getLogger;

Expand Down Expand Up @@ -176,7 +177,7 @@ public void render( final LineStyling styling, final Geometry geom ) {
return;
}
Geometry renderGeometry = null;
if ( isPathGenerationExpensive( styling ) ) {
if ( isGenerationExpensive( styling ) ) {
renderGeometry = transformToWorldCrsAndClip( geom );
if ( renderGeometry == null ) {
return;
Expand All @@ -198,10 +199,6 @@ public void render( final LineStyling styling, final Geometry geom ) {
}
}

private boolean isPathGenerationExpensive( LineStyling styling ) {
return styling.stroke != null && styling.stroke.stroke != null;
}

@Override
public void render( final PolygonStyling styling, final Geometry geom ) {
if ( geom == null ) {
Expand All @@ -214,8 +211,8 @@ public void render( final PolygonStyling styling, final Geometry geom ) {
LOG.warn( "Trying to render line with polygon styling." );
}
Geometry renderGeometry = null;
if ( isPathGenerationExpensive( styling ) ) {
renderGeometry = transformToWorldCrsAndClip( renderGeometry );
if ( isGenerationExpensive( styling ) ) {
renderGeometry = transformToWorldCrsAndClip( geom );
if ( renderGeometry == null ) {
return;
}
Expand All @@ -235,10 +232,6 @@ public void render( final PolygonStyling styling, final Geometry geom ) {
}
}

private boolean isPathGenerationExpensive( PolygonStyling styling ) {
return styling.stroke != null && styling.stroke.stroke != null;
}

@Override
public void render( Styling styling, Geometry geom ) {
if ( geom instanceof GeometryReference<?> ) {
Expand Down Expand Up @@ -275,6 +268,10 @@ public void render( Styling styling, Collection<Geometry> geoms ) {

Geometry transformToWorldCrsAndClip( final Geometry geom ) {
final Geometry geomInWorldCrs = rendererContext.geomHelper.transform( geom );
if ( rendererContext.clipper == null ) {
LOG.warn( "No clipper defined, geometry will be ignored for rendering" );
return null;
}
return rendererContext.clipper.clipGeometry( geomInWorldCrs );
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ public void testPointStyling()
style.graphic.displacementX = 16;
style.graphic.displacementY = 16;
r.render( style, geomFac.createPoint( null, new double[] { 4000, 4000 }, mapcs ) );

g.dispose();
long time2 = currentTimeMillis();
List<String> texts = new LinkedList<String>();
Expand Down Expand Up @@ -885,4 +885,44 @@ public void testPolygonStylingPerpendicularOffset()
texts.add( "polygon: white rectangle with red triangle stroke and perpendicular offest of -4. Expected: triangles points to the INSIDE of the geometry!" );
writeTestImage( img, texts, time2 - time );
}

/**
* Prevent reintroducing of a clipping error on extra large geometries
*
* Prevent endless dash generation in rendering of strokes (or JVM Crashes in Tomcat)
* if one of the coordinates of a geometry has an invalid value (which is out of bounds)
* and the clipper does not clip the geometry
*
* A timeout is required to prevent a endless runtime in JUnit test runner
*
* @author <a href="mailto:reichhelm@grit.de">Stephan Reichhelm</a>
* @throws Exception
*/
@Test(timeout=30000)
public void testClipperJvmCrash() throws Exception {
BufferedImage img = new BufferedImage( 100, 100, TYPE_INT_ARGB );
Graphics2D g = img.createGraphics();
GeometryFactory geomFac = new GeometryFactory();

double cen0 = 642800d;
double cen1 = 5600049000d;

Java2DRenderer r = new Java2DRenderer( g, img.getWidth(), img.getHeight(),
geomFac.createEnvelope( new double[] { 0, 0 },
new double[] { 50d, 50d }, mapcs ) );
Point p1 = geomFac.createPoint( "testP1", 0, 0, null );
Point p2 = geomFac.createPoint( "testP1", cen0, cen1, null );
Points points = new PointsArray( p1, p2 );

LineString lineString = geomFac.createLineString( "testLineString", null, points );

LineStyling styling = new LineStyling();
styling.stroke.color = red;
styling.stroke.width = 3;
styling.stroke.dasharray = new double[] { 10.0d, 10.0d };

r.render( styling, lineString );

g.dispose();
}
}