/*
* Copyright (C) 2015 Stichting Akvo (Akvo Foundation)
*
* This file is part of Akvo Flow.
*
* Akvo Flow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Akvo Flow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Akvo Flow. If not, see <http://www.gnu.org/licenses/>.
*/
package org.akvo.flow.ui.map;
import android.location.Location;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Polygon;
import com.google.android.gms.maps.model.PolygonOptions;
import com.google.maps.android.geometry.Point;
import org.akvo.flow.R;
import org.akvo.flow.util.GeoUtil;
import java.util.ArrayList;
import java.util.List;
public class PolygonFeature extends Feature {
public static final String GEOMETRY_TYPE = "Polygon";
private static final int FILL_COLOR = 0x88736357;
private static final int EARTH_RADIUS = 6371000; // meters
private static final double LATITUDE_SIZE = Math.PI * EARTH_RADIUS / 180;
private Polygon mPolygon;
public PolygonFeature(GoogleMap map) {
super(map);
}
@Override
public void addPoint(LatLng point) {
super.addPoint(point);
if (mPolygon == null) {
PolygonOptions polygonOptions = new PolygonOptions();
polygonOptions.strokeColor(mSelected ? STROKE_COLOR_SELECTED : STROKE_COLOR_DEFAULT);
polygonOptions.fillColor(FILL_COLOR);
polygonOptions.add(point);// Polygon cannot be created without points
mPolygon = mMap.addPolygon(polygonOptions);
} else {
mPolygon.setPoints(mPoints);
}
}
@Override
public void removePoint() {
super.removePoint();
if (mPoints.isEmpty()) {
mPolygon.remove();
mPolygon = null;
} else {
mPolygon.setPoints(mPoints);
}
}
@Override
public void delete() {
super.delete();
if (mPolygon != null) {
mPolygon.remove();
}
}
@Override
public void invalidate() {
super.invalidate();
if (mPolygon != null) {
mPolygon.setStrokeColor(mSelected ? STROKE_COLOR_SELECTED : STROKE_COLOR_DEFAULT);
if (!mPoints.isEmpty()) {
mPolygon.setPoints(mPoints);
}
}
// ---- Properties ----
// Length
float length = 0f;
// Init previous with last point, so we compute the last-first distance as well
LatLng previous = mPoints.size() > 2 ? mPoints.get(mPoints.size()-1) : null;
for (LatLng point : mPoints) {
if (previous != null) {
float[] distance = new float[1];
Location.distanceBetween(previous.latitude, previous.longitude, point.latitude, point.longitude, distance);
length += distance[0];
}
previous = point;
}
String lengthVal = String.format("%.2f", length);
mProperties.add(new Property("length", lengthVal, "Length", GeoUtil.getDisplayLength(length)));
// Area
final double area = area();
String areaVal = String.format("%.2f", area);
mProperties.add(new Property("area", areaVal, "Area", GeoUtil.getDisplayArea(area)));
}
@Override
public int getTitle() {
return R.string.geoshape_area;
}
@Override
public String geoGeometryType() {
return GEOMETRY_TYPE;
}
@Override
public boolean highlightNext(int position) {
return true;
}
private double area() {
if (mPoints.size() < 3) {
return 0f;
}
// First we compute an equal-are projection of the polygon. We use a sinusoidal
// projection for now: http://en.wikipedia.org/wiki/Sinusoidal_projection
List<Point> points = new ArrayList<>();
for (LatLng location : mPoints) {
points.add(project(location));
}
// Now we calculate the area, using regular planar techniques.
// http://mathworld.wolfram.com/PolygonArea.html
double area = 0.0;
Point prev = points.get(points.size()-1);// start form the last point
int i = 0;
while (i < points.size()) {
Point point = points.get(i);
area += prev.x * point.y - point.x * prev.y;
prev = point;
i++;
}
return Math.abs(area) / 2;
}
private Point project(LatLng location) {
// Sinusoidal projection (equal-area)
double y = location.latitude * LATITUDE_SIZE;
double x = location.longitude * LATITUDE_SIZE * Math.cos(Math.toRadians(location.latitude));
return new Point(x, y);
}
}