diff --git a/modules/library/main/src/main/java/org/geotools/geometry/jts/LiteShape2.java b/modules/library/main/src/main/java/org/geotools/geometry/jts/LiteShape2.java
index 3fa78c0e95b..d31bc7c540a 100644
--- a/modules/library/main/src/main/java/org/geotools/geometry/jts/LiteShape2.java
+++ b/modules/library/main/src/main/java/org/geotools/geometry/jts/LiteShape2.java
@@ -222,6 +222,12 @@ public void setGeometry(Geometry g) throws TransformException,
transformGeometry(geometry);
}
}
+
+ public void setGeometry2(Geometry g){
+ if (g != null) {
+ this.geometry = g;
+ }
+ }
/**
* Tests if the interior of the Shape
entirely contains the
diff --git a/modules/library/main/src/main/java/org/geotools/styling/visitor/UomRescaleStyleVisitor.java b/modules/library/main/src/main/java/org/geotools/styling/visitor/UomRescaleStyleVisitor.java
index 1bc593c5c5a..5c5d8f9f5fc 100644
--- a/modules/library/main/src/main/java/org/geotools/styling/visitor/UomRescaleStyleVisitor.java
+++ b/modules/library/main/src/main/java/org/geotools/styling/visitor/UomRescaleStyleVisitor.java
@@ -216,6 +216,9 @@ public void visit(LineSymbolizer line) {
Stroke copyStroke = copy.getStroke();
rescaleStroke(copyStroke, mapScale, copy.getUnitOfMeasure());
copy.setUnitOfMeasure(NonSI.PIXEL);
+
+ Expression copyPerpendicularOffset = copy.getPerpendicularOffset();
+ rescale(copyPerpendicularOffset, mapScale, copy.getUnitOfMeasure());
}
@Override
diff --git a/modules/library/render/src/main/java/org/geotools/renderer/lite/StyledShapePainter.java b/modules/library/render/src/main/java/org/geotools/renderer/lite/StyledShapePainter.java
index ab0414c5fbd..37d8cdb98b8 100644
--- a/modules/library/render/src/main/java/org/geotools/renderer/lite/StyledShapePainter.java
+++ b/modules/library/render/src/main/java/org/geotools/renderer/lite/StyledShapePainter.java
@@ -53,8 +53,11 @@
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.MultiPolygon;
import com.vividsolutions.jts.geom.Polygon;
+import com.vividsolutions.jts.operation.buffer.BufferParameters;
+import com.vividsolutions.jts.operation.buffer.OffsetCurveBuilder;
/**
* A simple class that knows how to paint a Shape object onto a Graphic given a
@@ -113,7 +116,7 @@ public void paint(final Graphics2D graphics, final LiteShape2 shape,
* @throws FactoryException
* @throws TransformException
*/
- public void paint(final Graphics2D graphics, final LiteShape2 shape,
+ public void paint(final Graphics2D graphics, LiteShape2 shape,
final Style2D style, final double scale, boolean isLabelObstacle) {
if (style == null) {
// TODO: what's going on? Should not be reached...
@@ -259,6 +262,23 @@ public void paint(final Graphics2D graphics, final LiteShape2 shape,
if (style instanceof LineStyle2D) {
LineStyle2D ls2d = (LineStyle2D) style;
+
+ double perpendicularOffset = ls2d.getPerpendicularOffset();
+ if (Math.abs(perpendicularOffset) != 0){
+ GeometryFactory factory = new GeometryFactory();
+ LineString lineString = factory.createLineString(shape.getGeometry().getCoordinates());
+ BufferParameters bufParams = new BufferParameters();
+ bufParams.setQuadrantSegments(0);
+ bufParams.setJoinStyle(BufferParameters.CAP_FLAT);
+ OffsetCurveBuilder offsetCurveBuilder = new OffsetCurveBuilder(lineString.getPrecisionModel(), bufParams);
+ Coordinate[] coordinates = offsetCurveBuilder.getOffsetCurve(lineString.getCoordinates(), perpendicularOffset);
+ LineString offsetLineString = shape.getGeometry().getFactory().createLineString(coordinates);
+ try {
+ shape = new LiteShape2(offsetLineString, null, null, false);
+ } catch (TransformException e) {
+ } catch (FactoryException e) {
+ }
+ }
if (ls2d.getStroke() != null) {
// see if a graphic stroke is to be used, the drawing method
diff --git a/modules/library/render/src/main/java/org/geotools/renderer/style/LineStyle2D.java b/modules/library/render/src/main/java/org/geotools/renderer/style/LineStyle2D.java
index 8bd25fd4b01..ab10ce410ff 100644
--- a/modules/library/render/src/main/java/org/geotools/renderer/style/LineStyle2D.java
+++ b/modules/library/render/src/main/java/org/geotools/renderer/style/LineStyle2D.java
@@ -37,6 +37,7 @@
public class LineStyle2D extends Style2D {
protected Paint contour;
protected Stroke stroke;
+ protected double perpendicularOffset;
protected Composite contourComposite;
/** Holds value of property graphicStroke. */
@@ -61,6 +62,14 @@ public Stroke getStroke() {
public void setStroke(Stroke stroke) {
this.stroke = stroke;
}
+
+ public double getPerpendicularOffset() {
+ return perpendicularOffset;
+ }
+
+ public void setPerpendicularOffset(double perpendicularOffset) {
+ this.perpendicularOffset = perpendicularOffset;
+ }
/**
* Returns the contour color for the {@linkplain org.geotools.renderer.geom.Polyline polyline}
diff --git a/modules/library/render/src/main/java/org/geotools/renderer/style/SLDStyleFactory.java b/modules/library/render/src/main/java/org/geotools/renderer/style/SLDStyleFactory.java
index eeae7c68ed1..0326b0bf6be 100644
--- a/modules/library/render/src/main/java/org/geotools/renderer/style/SLDStyleFactory.java
+++ b/modules/library/render/src/main/java/org/geotools/renderer/style/SLDStyleFactory.java
@@ -462,6 +462,7 @@ Style2D createLineStyle(Object feature, LineSymbolizer symbolizer,
LineStyle2D style = new LineStyle2D();
setScaleRange(style, scaleRange);
style.setStroke(getStroke(symbolizer.getStroke(), feature));
+ style.setPerpendicularOffset(evalToDouble(symbolizer.getPerpendicularOffset(), feature, 0));
style.setGraphicStroke(getGraphicStroke(symbolizer.getStroke(),
feature, scaleRange));
style.setContour(getStrokePaint(symbolizer.getStroke(), feature));
diff --git a/modules/library/render/src/test/java/org/geotools/renderer/lite/PerpendicularOffsetTest.java b/modules/library/render/src/test/java/org/geotools/renderer/lite/PerpendicularOffsetTest.java
new file mode 100644
index 00000000000..7b9ca12dba9
--- /dev/null
+++ b/modules/library/render/src/test/java/org/geotools/renderer/lite/PerpendicularOffsetTest.java
@@ -0,0 +1,79 @@
+package org.geotools.renderer.lite;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+
+import org.geotools.data.property.PropertyDataStore;
+import org.geotools.data.simple.SimpleFeatureSource;
+import org.geotools.geometry.jts.ReferencedEnvelope;
+import org.geotools.map.FeatureLayer;
+import org.geotools.map.Layer;
+import org.geotools.map.MapContent;
+import org.geotools.referencing.crs.DefaultGeographicCRS;
+import org.geotools.styling.Style;
+import org.geotools.test.TestData;
+import org.junit.Before;
+import org.junit.Test;
+
+public class PerpendicularOffsetTest {
+
+ private static final long TIME = 8000;
+
+ StreamingRenderer renderer;
+ ReferencedEnvelope bounds;
+
+ @Before
+ public void initialize() throws Exception {
+ renderer = new StreamingRenderer();
+ bounds = new ReferencedEnvelope(0, 10, 0, 10, DefaultGeographicCRS.WGS84);
+
+ //System.setProperty("org.geotools.test.interactive", "true");
+ }
+
+ SimpleFeatureSource getData(String name) throws IOException, URISyntaxException {
+ File property = new File(TestData.getResource(this, name + ".properties").toURI());
+ PropertyDataStore ds = new PropertyDataStore(property.getParentFile());
+ SimpleFeatureSource fs = ds.getFeatureSource(name);
+ return fs;
+ }
+
+ void setupRenderer(String name, Style style) throws IOException, URISyntaxException {
+ MapContent mc = new MapContent();
+ Layer layer = new FeatureLayer(getData(name), style);
+ mc.addLayer(layer);
+ renderer.setMapContent(mc);
+ }
+
+ @Test
+ public void testDiagonalPerpendicularOffset() throws Exception {
+ Style style = RendererBaseTest.loadStyle(this, "linePerpendicularOffset.sld");
+
+ setupRenderer("diaglines", style);
+
+ RendererBaseTest.showRender("Diagonal lines PerpendicularOffset", renderer, TIME, bounds);
+ }
+
+ @Test
+ public void testCurvedPerpendicularOffset() throws Exception {
+ Style style = RendererBaseTest.loadStyle(this, "linePerpendicularOffset.sld");
+
+ setupRenderer("curvedline", style);
+
+ RendererBaseTest.showRender("Curved line PerpendicularOffset", renderer, TIME, bounds);
+ }
+
+ /**
+ * TODO: This test shows a case where the current PerpendicularOffset implementation does not produce a
+ * valid result.
+ */
+ @Test
+ public void testNarrowCurvedPerpendicularOffset() throws Exception {
+ Style style = RendererBaseTest.loadStyle(this, "linePerpendicularOffsetWide.sld");
+
+ setupRenderer("narrowcurvedline", style);
+
+ RendererBaseTest.showRender("Narrow curved line wide PerpendicularOffset", renderer, TIME, bounds);
+ }
+
+}
diff --git a/modules/library/render/src/test/resources/org/geotools/renderer/lite/test-data/curvedline.properties b/modules/library/render/src/test/resources/org/geotools/renderer/lite/test-data/curvedline.properties
new file mode 100644
index 00000000000..e02cdfb811d
--- /dev/null
+++ b/modules/library/render/src/test/resources/org/geotools/renderer/lite/test-data/curvedline.properties
@@ -0,0 +1,2 @@
+_=geom:LineString:4326
+Line.1=LINESTRING(1 5, 4 8, 7 6, 6 3, 3 2)
\ No newline at end of file
diff --git a/modules/library/render/src/test/resources/org/geotools/renderer/lite/test-data/linePerpendicularOffset.sld b/modules/library/render/src/test/resources/org/geotools/renderer/lite/test-data/linePerpendicularOffset.sld
new file mode 100644
index 00000000000..59de7182c6e
--- /dev/null
+++ b/modules/library/render/src/test/resources/org/geotools/renderer/lite/test-data/linePerpendicularOffset.sld
@@ -0,0 +1,35 @@
+
+
+
+
+ GrayLines
+
+
+
+
+
+ 0x000000
+ 1
+
+
+
+
+ 0xff0000
+ 1
+
+ 3
+
+
+
+ 0x0000ff
+ 1
+
+ -3
+
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/library/render/src/test/resources/org/geotools/renderer/lite/test-data/linePerpendicularOffsetWide.sld b/modules/library/render/src/test/resources/org/geotools/renderer/lite/test-data/linePerpendicularOffsetWide.sld
new file mode 100644
index 00000000000..813ffa11be4
--- /dev/null
+++ b/modules/library/render/src/test/resources/org/geotools/renderer/lite/test-data/linePerpendicularOffsetWide.sld
@@ -0,0 +1,28 @@
+
+
+
+
+ GrayLines
+
+
+
+
+
+ 0x000000
+ 1
+
+
+
+
+ 0xff0000
+ 1
+
+ 5
+
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/library/render/src/test/resources/org/geotools/renderer/lite/test-data/narrowcurvedline.properties b/modules/library/render/src/test/resources/org/geotools/renderer/lite/test-data/narrowcurvedline.properties
new file mode 100644
index 00000000000..451b101ae56
--- /dev/null
+++ b/modules/library/render/src/test/resources/org/geotools/renderer/lite/test-data/narrowcurvedline.properties
@@ -0,0 +1,2 @@
+_=geom:LineString:4326
+Line.1=LINESTRING(1 5, 4 8, 7 6, 5.5 6.9, 3 2)
\ No newline at end of file