I've found a little tricky solution: Need to use my reference system in the WKT coordinates.
First I need a function to calculate a new point using a point, an azimuth and distance as input:
public static Point projectPoint( double lon, double lat, double distance, double azimuth ) {
System.out.println("> From: " + lon + "," + lat + " Az: " + azimuth + " Dist: " + distance );
DefaultGeographicCRS crs = DefaultGeographicCRS.WGS84;
GeodeticCalculator calc = new GeodeticCalculator(crs);
GeometryFactory geomFactory = new GeometryFactory();
Point point = geomFactory.createPoint( new Coordinate(lon, lat) );
calc.setStartingGeographicPoint(point.getX(), point.getY());
calc.setDirection(azimuth, distance);
Point2D p = calc.getDestinationGeographicPoint();
Point result = geomFactory.createPoint( new Coordinate(p.getX(), p.getY() ) );
System.out.println(" > To: " + result.getX() + "," + result.getY() );
return result;
}
Now I need another function to draw the polygon I need, projecting points one from another starting at the reference coordinates ( the point geometry that will use this SLD ). This will take the reference point coordinates, width, length and bearing of the "ship" and draw a rectangle projecting its corner points.
private static Polygon getHull( double lon, double lat, double width, double length, double bearing ) {
GeometryFactory geomFactory = new GeometryFactory();
Point p0 = geomFactory.createPoint( new Coordinate(lon, lat) );
Point p1 = PointProjector.projectPoint( lon, lat, width, bearing + 90);
Point p2 = PointProjector.projectPoint(p1.getX(), p1.getY(), length, bearing + 0 );
Point p3 = PointProjector.projectPoint(p2.getX(), p2.getY(), width, bearing + 270 );
Point p4 = p0;
Coordinate[] coords = new Coordinate[] { p0.getCoordinate(), p1.getCoordinate(),p2.getCoordinate(),p3.getCoordinate(),p4.getCoordinate() };
LinearRing ring = geomFactory.createLinearRing( coords );
LinearRing holes[] = null;
Polygon polygon = geomFactory.createPolygon(ring, holes );
return polygon;
}
Now I prefer to have another function to draw a MultiPolygon if a complex draw becomes necessary. This will draw the "ship" using separate parts (only a primitive hull for now).
public static String createShip( double lon, double lat, double width, double length, double bearing ) {
GeometryFactory geomFactory = new GeometryFactory();
Polygon hull = getHull( lon, lat, width, length, bearing );
Polygon[] polygons = new Polygon[] { hull };
MultiPolygon mp = geomFactory.createMultiPolygon( polygons );
return mp.toText();
}
Now I have a WKT MultiPolygon representing my "ship" going to 45 deg:
System.out.println( ShipCreator.createShip(-43, -23, 1000, 5000, 45) );
The output:
select
ST_GeomFromText(
'MULTIPOLYGON (((-43 -23, -42.993102589219724 -23.006384903984472, -42.95862364344146 -22.974455829056822, -42.965519108985276 -22.96807059921952, -43 -23)))'
, 4326);
... and the result:
The SLD must have a Property as the WKT content to allow pass it from the code (here called "wktgeom"):
<PointSymbolizer uom="http://www.opengeospatial.org/se/units/metre">
<Graphic>
<Mark>
<WellKnownName><PropertyName>wktgeom</PropertyName></WellKnownName>
<Fill>
<CssParameter name="fill">#009900</CssParameter>
<CssParameter name="fill-opacity">0.4</CssParameter>
</Fill>
</Mark>
<Rotation><PropertyName>bearing</PropertyName></Rotation>
</Graphic>
</PointSymbolizer>
And the code:
Point point = this.vesselGeometryFactory.createPoint(new Coordinate(x, y));
this.vesselFeatureBuilder.set("coordinates", point);
this.vesselFeatureBuilder.set("lon", x);
this.vesselFeatureBuilder.set("lat", y);
this.vesselFeatureBuilder.set("length", 100);
this.vesselFeatureBuilder.set("width", 30);
this.vesselFeatureBuilder.set("bearing", bearing );
this.vesselFeatureBuilder.set("mmsi", mmsi);
String wktContent = ShipCreator.createShip( x, y, 50, 100, 45) ;
this.vesselFeatureBuilder.set("wktgeom", "wkt://" + wktContent);
SimpleFeature aVessel = this.vesselFeatureBuilder.buildFeature( String.valueOf( mmsi ) );
Obviously you need to assign the style to the layer:
Style style = getStyle( "vesselStyle.sld" );
this.vesselLayer = new FeatureLayer( this.vesselCollection, style );
I don't know if I must "turn" the polygon right in the getHull()
function or draw it always pointing to north and use the tag <Rotation>
to do the job ...
I hope I didn't do anything too scary !! Please be gentle in comments because it cost me a lot of effort.
EDIT
An alternative to this method is to pass the MultiPolygon geometry to the feature. Change the Feature geometry:
Polygon[] theShipGeom = ShipCreator.createShip( x, y, 50, 100, bearing) ;
MultiPolygon ship = this.vesselGeometryFactory.createMultiPolygon( theShipGeom );
And pass it :
this.vesselFeatureBuilder.set("coordinate", ship);
Or course you'll need to change the schema:
builder.add("coordinate", MultiPolygon.class);
This way you won't need to care about WKT in SLD.