Stas Rodov
Stas Rodov

Reputation: 23

JavaFX: Add Maps And Draw On Top Of It

I'm building a JavaFx application and trying to embed Google Maps in it, using GMapsFx.

I'm trying to draw ellipses om top of the map using this API, but the API only provides arcs.
So in order to work around this I tried to draw four quarters of an ellipse, but managed to draw only four quarters of a circle, it seemed that GMapsFx ignores one of the radii and draws a circular arc.

The result of the following code snippet:

double centerLat = 31.166724;
double centerLong = 34.793119;
double latDelta = 0.5;
double longDelta = 0.01;
LatLong centreP = new LatLong(centerLat, centerLong);
LatLong start = new LatLong(centerLat, centerLong + longDelta);
LatLong end= new LatLong(centerLat + latDelta, centerLong);
MVCArray p= ArcBuilder.buildArcPoints(centreP, end, start);

com.lynden.gmapsfx.shapes.Polyline pp = new com.lynden.gmapsfx.shapes.Polyline();
pp.setPath(p);
map.addMapShape(pp);

Appears in the following image.

Result of the code

Digesting the numbers:
Center: 31.166724, 34.793119
Start: 31.166724, 34.803119
End: 31.666724, 34.793119

You can see that end and center are very close to each other (we should have gotten a very high and narrow arc), the arc actually starts very far in the east giving quarter of a circle.

  1. What causes this issue?
  2. Is there a better way\framework to draw ellipses on top of maps in JavaFx?

Upvotes: 2

Views: 1717

Answers (1)

Modus Tollens
Modus Tollens

Reputation: 5123

The ArcBuilder only uses one radius; the start and end LatLong is used to get the bearings of the start and end point of the arc. Therefore it is not usable for drawing an ellipse.

Since the library doesn't provide ellipse shapes, I wrote an EllipseBuilder class instead:

import com.lynden.gmapsfx.javascript.object.LatLong;
import com.lynden.gmapsfx.javascript.object.MVCArray;

public class EllipseBuilder {

  private static final int DEFAULT_ELLIPSE_POINTS = 10000;

  /**
   * Generates the points for an ellipse based on two radii from a center point.
   *
   * @param center
   *            The LatLong point of the center.
   * @param longRadius
   *            longitude radius in meters
   * @param latRadius
   *            latitude radius in meters
   * @return An array of LatLong points in an MVC array representing the ellipse.
   */
  public static final MVCArray buildEllipsePoints(LatLong center, double longRadius, double latRadius) {
    return buildEllipsePoints(center, longRadius, latRadius, 0.);
  }

  /**
   * Generates the points for an ellipse based on two radii from a center point and a counter clockwise rotation angle.
   *
   * @param center
   *            The LatLong point of the center.
   * @param longRadius
   *            longitude radius in meters
   * @param latRadius
   *            latitude radius in meters
   * @param rotAngle
   *            rotation angle in degree, counter clockwise
   * @return An array of LatLong points in an MVC array representing the ellipse.
   */
  public static final MVCArray buildEllipsePoints(LatLong center, double longRadius, double latRadius, double rotAngle) {
    int points = DEFAULT_ELLIPSE_POINTS;

    MVCArray res = new MVCArray();

    double longRadiusSquared = longRadius * longRadius;
    double latRadiusSquared = latRadius * latRadius;

    double radiiProduct = longRadius * latRadius;

    double theta = 0d;
    double angleIncrement = 360.0 / points;
    for (int i = 0; (i < points + 1); i++) {
        theta = i * angleIncrement;
        double r = radiiProduct / (Math.sqrt(latRadiusSquared * Math.pow(Math.sin(Math.toRadians(theta)), 2)
                + longRadiusSquared * Math.pow(Math.cos(Math.toRadians(theta)), 2)));
        res.push(center.getDestinationPoint(theta - rotAngle, r));
    }

    return res;
  }
}

Calling it using the following code

double centerLat = 31.166724;
double centerLong = 34.793119;
double latDelta = 0.5;
double longDelta = 0.01;
LatLong centreP = new LatLong(centerLat, centerLong);
LatLong start = new LatLong(centerLat, centerLong + longDelta);
LatLong end = new LatLong(centerLat + latDelta, centerLong);

double longRadius = centreP.distanceFrom(start);
double latRadius = centreP.distanceFrom(end);

MVCArray p = EllipseBuilder.buildEllipsePoints(centreP, longRadius, latRadius);

com.lynden.gmapsfx.shapes.Polyline pp = new com.lynden.gmapsfx.shapes.Polyline();
pp.setPath(p);
map.addMapShape(pp);

// Add markers to the map
MarkerOptions markerOptionsStart = new MarkerOptions();
markerOptionsStart.position(start).visible(Boolean.TRUE).title("Start").label("S");
Marker markerStart = new Marker(markerOptionsStart);
map.addMarker(markerStart);

MarkerOptions markerOptionsEnd = new MarkerOptions();
markerOptionsEnd.position(end).visible(Boolean.TRUE).title("End").label("E");
Marker markerEnd = new Marker(markerOptionsEnd);
map.addMarker(markerEnd);

results in this image:

GMapFX Ellipse Example

To draw a rotated ellipse, add an angle in degree. The ellipse will be rotated in counter clockwise direction:

double longRadius = centreP.distanceFrom(start);
double latRadius = centreP.distanceFrom(end);
double rotAngle = 20.;

MVCArray p = EllipseBuilder.buildEllipsePoints(centreP, longRadius, latRadius, rotAngle);

Ellipse rotated by 20 degrees, counter clockwise:

GMapFX Rotated Ellipse Example

I used markers to mark the start and end point.

Because the ellipse is very narrow in your case, I used a very high value for DEFAULT_ELLIPSE_POINTS. Depending on the use case this value should be smaller, so it might be useful to set it as a parameter instead of a static variable.

Upvotes: 1

Related Questions