4

After solvig this question, seems WKT is not respecting my intent to use "metre" as measure of distance.

I have this SLD:

      <FeatureTypeStyle>
        <Rule>
          <PointSymbolizer uom="http://www.opengeospatial.org/se/units/metre">
            <Graphic>
              <Mark>
                <WellKnownName>wkt://POLYGON ((0 0, ${width} 0, ${width} ${length}, 0 ${length}, 0 0))</WellKnownName>
                <Fill>
                  <CssParameter name="fill">#009900</CssParameter>
                  <CssParameter name="fill-opacity">0.4</CssParameter>
                </Fill>
              </Mark>
              <Rotation><PropertyName>bearing</PropertyName></Rotation>
            </Graphic>
          </PointSymbolizer>
        </Rule>
        <Rule>
          <PointSymbolizer uom="http://www.opengeospatial.org/se/units/metre">
            <Graphic>
              <Mark>
                <WellKnownName>circle</WellKnownName>
                <Fill>
                  <CssParameter name="fill">#f00505</CssParameter>
                </Fill>
              </Mark>
              <Size>10</Size>
            </Graphic>
          </PointSymbolizer>
        </Rule>     
      </FeatureTypeStyle>

Consider width=30 and length=100.

I can see the red point (seems to) respect a 10 metres radius, but the green rectangles do not.

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

How can I draw a Polygon in wkt using ground units? May I calculate all coordinates before pass it to the SLD? if so, how can I pass the entire wkt as a String?

1 Answer 1

3

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:

enter image description here

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.

1
  • If you use this method as a "pen" and remove the SLD part you can draw anything you want.
    – Magno C
    Commented Apr 5, 2023 at 14:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.