6 package com.google.appinventor.components.runtime.util;
10 import org.json.JSONArray;
11 import org.json.JSONException;
12 import org.locationtech.jts.geom.Coordinate;
13 import org.locationtech.jts.geom.Geometry;
14 import org.locationtech.jts.geom.GeometryFactory;
15 import org.locationtech.jts.geom.LineString;
16 import org.locationtech.jts.geom.LinearRing;
17 import org.locationtech.jts.geom.Point;
18 import org.locationtech.jts.geom.Polygon;
19 import org.locationtech.jts.geom.PrecisionModel;
20 import org.osmdroid.api.IGeoPoint;
27 import org.osmdroid.util.GeoPoint;
29 import java.util.ArrayList;
30 import java.util.Iterator;
31 import java.util.LinkedList;
32 import java.util.List;
40 @SuppressWarnings(
"WeakerAccess")
43 public static final double EARTH_RADIUS = 6378137.0;
44 public static final double ONE_DEG_IN_METERS = EARTH_RADIUS * Math.PI / 180.0;
45 public static final int WEB_MERCATOR_SRID = 4326;
46 private static final GeometryFactory FACTORY =
47 new GeometryFactory(
new PrecisionModel(), WEB_MERCATOR_SRID);
53 if (o instanceof Number) {
54 return ((Number) o).doubleValue();
57 return Double.parseDouble(o.toString());
58 }
catch(NumberFormatException e) {
65 double latitude = coerceToDouble(lat);
66 double longitude = coerceToDouble(lng);
67 if (Double.isNaN(latitude)) {
68 throw new IllegalArgumentException(
"Latitude must be a numeric.");
69 }
else if (Double.isNaN(longitude)) {
70 throw new IllegalArgumentException(
"Longitude must be a numeric.");
71 }
else if (latitude < -90.0 || latitude > 90.0) {
72 throw new IllegalArgumentException(
"Latitude must be between -90 and 90.");
73 }
else if (longitude < -180.0 || longitude > 180.0) {
74 throw new IllegalArgumentException(
"Longitude must be between -180 and 180.");
76 return new GeoPoint(latitude, longitude);
81 return YailList.
makeList(
new Object[] { point.getLatitude(), point.getLongitude() });
85 List<YailList> entries =
new ArrayList<YailList>();
86 for (IGeoPoint point : points) {
87 entries.add(asYailList(point));
93 if (point.length() < 3) {
95 2, point.length() - 1);
99 }
catch(IllegalArgumentException e) {
105 List<GeoPoint> newPoints =
new ArrayList<GeoPoint>();
109 while (it.hasNext()) {
121 return FACTORY.createPoint(geoPointToCoordinate(point));
125 return FACTORY.createLineString(pointsToCoordinates(line));
128 public static Geometry
createGeometry(
double north,
double east,
double south,
double west) {
129 return FACTORY.createPolygon(
new Coordinate[]{
130 new Coordinate(east, north),
131 new Coordinate(east, south),
132 new Coordinate(west, south),
133 new Coordinate(west, north),
134 new Coordinate(east, north)
138 public static Geometry
createGeometry(List<List<GeoPoint>> points, List<List<List<GeoPoint>>> holes) {
139 if (points ==
null) {
140 throw new IllegalArgumentException(
"points must not be null.");
142 if (holes !=
null && !holes.isEmpty() && holes.size() != points.size()) {
143 throw new IllegalArgumentException(
"holes must either be null or the same length as points.");
147 if (holes ==
null || holes.isEmpty()) {
148 for (List<GeoPoint> ring : points) {
149 polygons[i++] = ringToPolygon(ring);
152 Iterator<List<GeoPoint>> ip = points.iterator();
153 Iterator<List<List<GeoPoint>>> jp = holes.iterator();
154 while (ip.hasNext()) {
155 polygons[i++] = ringToPolygon(ip.next(), jp.next());
158 return polygons.length == 1 ? polygons[0] : FACTORY.createMultiPolygon(polygons);
162 if (points.isEmpty()) {
164 return new GeoPoint(0.0, 0.0);
165 }
else if (points.size() == 1) {
166 return new GeoPoint(points.get(0));
168 LineString ls = FACTORY.createLineString(pointsToCoordinates(points));
186 public static GeoPoint
getCentroid(List<List<GeoPoint>> points, List<List<List<GeoPoint>>> holes) {
187 return jtsPointToGeoPoint(createGeometry(points, holes).getCentroid());
191 return FACTORY.createPolygon(geoPointsToLinearRing(ring));
195 boolean closed = points.get(0).equals(points.get(points.size() - 1));
196 Coordinate[] coordinates =
new Coordinate[points.size() + (closed ? 0 : 1)];
198 for (GeoPoint p : points) {
199 coordinates[i++] = geoPointToCoordinate(p);
202 coordinates[i] = coordinates[0];
208 return FACTORY.createLinearRing(pointsToCoordinates(points));
212 LinearRing shell = geoPointsToLinearRing(ring);
213 LinearRing[] holeRings =
new LinearRing[holes.size()];
215 for (List<GeoPoint> h : holes) {
216 holeRings[i++] = geoPointsToLinearRing(h);
218 return FACTORY.createPolygon(shell, holeRings);
222 return new GeoPoint(p.getY(), p.getX());
226 return new Coordinate(p.getLongitude(), p.getLatitude());
230 double lat1 = Math.toRadians(a.getLatitude());
231 double lng1 = Math.toRadians(a.getLongitude());
232 double lat2 = Math.toRadians(b.getLatitude());
233 double lng2 = Math.toRadians(b.getLongitude());
234 double dLat = lat2 - lat1;
235 double dLng = lng2 - lng1;
236 double cordlen = Math.pow(Math.sin(dLat / 2), 2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(dLng / 2), 2);
237 double angle = 2 * Math.atan2(Math.sqrt(cordlen), Math.sqrt(1 - cordlen));
238 return EARTH_RADIUS * angle;
242 return distanceBetween(marker.
getLocation(), point);
259 return d < 0 ? 0 : d;
267 return ONE_DEG_IN_METERS * lineString.
getGeometry().distance(createGeometry(point));
280 return d < 0 ? 0 : d;
288 return ONE_DEG_IN_METERS * polygon.
getGeometry().distance(createGeometry(point));
297 return d < 0 ? 0 : d;
306 return d < 0 ? 0 : d;
311 return d < 0 ? 0 : d;
316 return d < 0 ? 0 : d;
320 return ONE_DEG_IN_METERS * rectangle.
getGeometry().distance(createGeometry(point));
344 return distanceBetween(lineString.
getCentroid(), point);
364 return distanceBetween(polygon.
getCentroid(), point);
380 return distanceBetween(circle.
getCentroid(), point);
392 return distanceBetween(rectangle.
getCentroid(), point);
445 return -90.0 <= latitude && latitude <= 90.0;
455 return -180.0 <= longitude && longitude <= 180.0;
458 public static List<GeoPoint>
polygonToList(JSONArray array)
throws JSONException {
459 List<GeoPoint> points =
new ArrayList<GeoPoint>(array.length());
460 if (array.length() < 3) {
462 "Too few points in Polygon, expected 3.");
464 for (
int i = 0; i < array.length(); i++) {
465 JSONArray point = array.getJSONArray(i);
466 if (point.length() < 2) {
467 throw new JSONException(
"Invalid number of dimensions in polygon, expected 2.");
469 if (point.length() == 2) {
470 points.add(
new GeoPoint(point.getDouble(0), point.getDouble(1)));
472 points.add(
new GeoPoint(point.getDouble(0), point.getDouble(1), point.getDouble(2)));
479 List<List<GeoPoint>> result =
new ArrayList<List<GeoPoint>>();
480 if (array.length() == 0) {
482 }
else if (array.getJSONArray(0).optJSONArray(0) ==
null) {
483 result.add(polygonToList(array));
485 for (
int i = 0; i < array.length(); i++) {
486 result.add(polygonToList(array.getJSONArray(i)));
493 List<YailList> result =
new LinkedList<YailList>();
494 for (List<GeoPoint> polygon : multipolygon) {
495 result.add(pointsListToYailList(polygon));
501 List<List<GeoPoint>> multipolygon =
new ArrayList<List<GeoPoint>>();
502 Iterator<?> it = list.listIterator(1);
503 while(it.hasNext()) {
510 List<List<List<GeoPoint>>> holes =
new ArrayList<List<List<GeoPoint>>>();
511 Iterator<?> it = points.listIterator(1);
514 while (it.hasNext()) {
516 holes.add(multiPolygonFromYailList(yailHoles));
526 List<List<List<GeoPoint>>> result =
new ArrayList<List<List<GeoPoint>>>();
527 if (array.getJSONArray(0).getJSONArray(0).optJSONArray(0) ==
null) {
528 result.add(multiPolygonToList(array));
530 for (
int i = 0; i < array.length(); i++) {
531 result.add(multiPolygonToList(array.getJSONArray(i)));
546 if (points.
size() < 3) {
567 return points.
size() > 0 &&