2

I would like to calculate the are of a polygon drawn in a map fragment for a college project.

This is how I draw my polygon.

@Override
    public void onMapClick(LatLng point) {
        //tvLocInfo.setText("New marker added@" + point.toString());
        map.addMarker(new MarkerOptions().position(point).draggable(true).title(point.toString()));

        markerClicked = false;
    }

    @Override
    public void onMapLongClick(LatLng point) {
        //tvLocInfo.setText("New marker added@" + point.toString());
        map.clear();
    }

    @Override
    public boolean onMarkerClick(Marker marker) {

        if(markerClicked){

            if(polygon != null){
                polygon.remove();
                polygon = null;
            }

            polygonOptions.add(marker.getPosition());
            polygonOptions.strokeColor(Color.RED);
            polygonOptions.fillColor(Color.BLUE);
            polygon = map.addPolygon(polygonOptions);
            //Area = google.maps.geometry.spherical.computeArea(polygon.getPath().getArray());
        }else{
            if(polygon != null){
                polygon.remove();
                polygon = null;
            }

            polygonOptions = new PolygonOptions().add(marker.getPosition());
            markerClicked = true;
        }

I have seen this code on how to calculate the area but I am unsure how to implement it in my application and calculate the area of my polygon.

I use this code to calculate an area of a GPS with Android:

  private static final double EARTH_RADIUS = 6371000;// meters

  public static double calculateAreaOfGPSPolygonOnEarthInSquareMeters(final List<Location> locations) {
    return calculateAreaOfGPSPolygonOnSphereInSquareMeters(locations, EARTH_RADIUS);
  }

  private static double calculateAreaOfGPSPolygonOnSphereInSquareMeters(final List<Location> locations, final double radius) {
    if (locations.size() < 3) {
      return 0;
    }

    final double diameter = radius * 2;
    final double circumference = diameter * Math.PI;
    final List<Double> listY = new ArrayList<Double>();
    final List<Double> listX = new ArrayList<Double>();
    final List<Double> listArea = new ArrayList<Double>();
    // calculate segment x and y in degrees for each point
    final double latitudeRef = locations.get(0).getLatitude();
    final double longitudeRef = locations.get(0).getLongitude();
    for (int i = 1; i < locations.size(); i++) {
      final double latitude = locations.get(i).getLatitude();
      final double longitude = locations.get(i).getLongitude();
      listY.add(calculateYSegment(latitudeRef, latitude, circumference));
      Log.d(LOG_TAG, String.format("Y %s: %s", listY.size() - 1, listY.get(listY.size() - 1)));
      listX.add(calculateXSegment(longitudeRef, longitude, latitude, circumference));
      Log.d(LOG_TAG, String.format("X %s: %s", listX.size() - 1, listX.get(listX.size() - 1)));
    }

    // calculate areas for each triangle segment
    for (int i = 1; i < listX.size(); i++) {
      final double x1 = listX.get(i - 1);
      final double y1 = listY.get(i - 1);
      final double x2 = listX.get(i);
      final double y2 = listY.get(i);
      listArea.add(calculateAreaInSquareMeters(x1, x2, y1, y2));
      Log.d(LOG_TAG, String.format("area %s: %s", listArea.size() - 1, listArea.get(listArea.size() - 1)));
    }

    // sum areas of all triangle segments
    double areasSum = 0;
    for (final Double area : listArea) {
      areasSum = areasSum + area;
    }

    // get abolute value of area, it can't be negative
    return Math.abs(areasSum);// Math.sqrt(areasSum * areasSum);
  }

  private static Double calculateAreaInSquareMeters(final double x1, final double x2, final double y1, final double y2) {
    return (y1 * x2 - x1 * y2) / 2;
  }

  private static double calculateYSegment(final double latitudeRef, final double latitude, final double circumference) {
    return (latitude - latitudeRef) * circumference / 360.0;
  }

  private static double calculateXSegment(final double longitudeRef, final double longitude, final double latitude,
      final double circumference) {
    return (longitude - longitudeRef) * circumference * Math.cos(Math.toRadians(latitude)) / 360.0;
  } 

I could also use the following polygon which is static if calculating the area of the drawn polygon is not possible.

Polygon UCCpolygon = map.addPolygon(new PolygonOptions()
    .add(new LatLng(51.893728, -8.491865), 
         new LatLng(51.893550, -8.492479), 
         new LatLng(51.893216, -8.492224), 
         new LatLng(51.893404, -8.491598))
 .strokeColor(Color.RED)
 .fillColor(Color.BLUE));

Thanks for the help!

Sean

duncan
  • 31,401
  • 13
  • 78
  • 99
nuhos10
  • 23
  • 1
  • 1
  • 3
  • First you need to get the `LatLng` from the `polygon`, use the `getHoles()`, the class is [here](https://developer.android.com/reference/com/google/android/gms/maps/model/Polygon.html). And use algorithm [here](https://answers.yahoo.com/question/index?qid=20080213175333AASYvUd), also you can refer to [here](http://stackoverflow.com/questions/9601816/calculate-earth-convex-hull-polygon-area-given-latitude-and-longitude). – bjiang Mar 03 '15 at 21:10
  • Hi,Thanks so much for the reply! public List> getHoles(){ return polygon.getHoles(); } Is this correct? Sorry but I do not know how to go about applying the algorithms you suggested! – nuhos10 Mar 04 '15 at 17:59

2 Answers2

16

There's already a library for that.

import com.google.maps.android.SphericalUtil;

//...

List<LatLng> latLngs = new ArrayList<>();
latLngs.add(new LatLng(51.893728, -8.491865));
latLngs.add(new LatLng(51.893550, -8.492479));
latLngs.add(new LatLng(51.893216, -8.492224));
latLngs.add(new LatLng(51.893404, -8.491598));
Log.i(TAG, "computeArea " + SphericalUtil.computeArea(latLngs));

For me the output is computeArea 1920.8879882782069

Marian Paździoch
  • 8,813
  • 10
  • 58
  • 103
  • Thanks for the answer. How can I apply it to the polygon I create with polygonOptions as seen here? I've been trying all day with no luck! – nuhos10 Mar 04 '15 at 18:11
  • the method takes a List of LatLng as parameter so apart of creating Polygon, create also a List out of the same LatLngs – Marian Paździoch Mar 04 '15 at 18:37
  • I have added the list of LatLng from my polygon and use the following code as you said but I am getting a value of 0 returned. `public double computeArea(java.util.List points){ return area; }` – nuhos10 Mar 04 '15 at 21:14
  • Then you're doing sth wrong - probably no LatLng was added to the list. Check my edited answer. – Marian Paździoch Mar 05 '15 at 08:16
  • The LatLng points are definitely in the list. This is the list `ArrayList points;` This is how I add the point then `public void onMapClick(LatLng point) { //tvLocInfo.setText("New marker added@" + point.toString()); map.addMarker(new MarkerOptions().position(point).draggable(true).title(point.toString())); points.add(point); Toast.makeText(this,""+ point, Toast.LENGTH_SHORT).show(); markerClicked = false; }` Is this correct? – nuhos10 Mar 05 '15 at 09:32
  • What is the measuring units return for the library? How can I get Hectar? – donmj Jan 03 '19 at 06:16
3

If you want to use SphericalUtils code without any library, you can use following code. it's taken from opensource code from SphericalUtils.java and other class. I have taken this code and used it as i was using MapBox and MapBox does not have implemented the calculateArea function in Turf.

import java.util.List;

import pojo.LatLng;

import static java.lang.Math.PI;
import static java.lang.Math.abs;
import static java.lang.Math.atan2;
import static java.lang.Math.cos;
import static java.lang.Math.sin;
import static java.lang.Math.tan;
import static java.lang.Math.toRadians;

public class PolygonUtils {

    /**
     * The earth's radius, in meters.
     * Mean radius as defined by IUGG.
     */
    static final double EARTH_RADIUS = 6371009;

    /**
     * Returns the area of a closed path on Earth.
     * @param path A closed path.
     * @return The path's area in square meters.
     */
    public static double computeArea(List<LatLng> path) {
        return abs(computeSignedArea(path,EARTH_RADIUS));
    }

    /**
     * Returns the signed area of a closed path on a sphere of given radius.
     * The computed area uses the same units as the radius squared.
     * Used by SphericalUtilTest.
     */
    static double computeSignedArea(List<LatLng> path, double radius) {
        int size = path.size();
        if (size < 3) { return 0; }
        double total = 0;
        LatLng prev = path.get(size - 1);
        double prevTanLat = tan((PI / 2 - toRadians(prev.getLatitude())) / 2);
        double prevLng = toRadians(prev.getLongitude());
        // For each edge, accumulate the signed area of the triangle formed by the North Pole
        // and that edge ("polar triangle").
        for (LatLng point : path) {
            double tanLat = tan((PI / 2 - toRadians(point.getLatitude())) / 2);
            double lng = toRadians(point.getLongitude());
            total += polarTriangleArea(tanLat, lng, prevTanLat, prevLng);
            prevTanLat = tanLat;
            prevLng = lng;
        }
        return total * (radius * radius);
    }

    /**
     * Returns the signed area of a triangle which has North Pole as a vertex.
     * Formula derived from "Area of a spherical triangle given two edges and the included angle"
     * as per "Spherical Trigonometry" by Todhunter, page 71, section 103, point 2.
     * See http://books.google.com/books?id=3uBHAAAAIAAJ&pg=PA71
     * The arguments named "tan" are tan((pi/2 - latitude)/2).
     */
    private static double polarTriangleArea(double tan1, double lng1, double tan2, double lng2) {
        double deltaLng = lng1 - lng2;
        double t = tan1 * tan2;
        return 2 * atan2(t * sin(deltaLng), 1 + t * cos(deltaLng));
    }
}
Jay Dangar
  • 3,271
  • 1
  • 16
  • 35