Skip to content

Commit b890450

Browse files
committed
fix: features and fixes
1 parent 0192989 commit b890450

File tree

15 files changed

+390
-8
lines changed

15 files changed

+390
-8
lines changed

plugin/platforms/android/java/com/akylas/carto/additions/AKMapView.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@ public class AKMapView extends MapView {
1616
public boolean userAction = false;
1717
private AKMapEventListener listener = null;
1818

19-
static boolean RUN_ON_MAIN_THREAD = true;
19+
static public boolean RUN_ON_MAIN_THREAD = true;
2020

21+
static public void setRunOnMainThread(boolean value) {
22+
RUN_ON_MAIN_THREAD = value;
23+
}
2124

2225
public void setMapEventListener(AKMapEventListener listener) {
2326
this.listener = listener;
Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
package com.akylas.carto.additions;
2+
3+
import com.carto.core.MapPosVector;
4+
import com.carto.core.MapPos;
5+
import com.carto.core.MapVec;
6+
7+
public class Utils {
8+
public static int EARTH_RADIUS = 6371009;
9+
public static double TO_RAD = Math.PI / 180;
10+
11+
public static MapPosVector decodeMapPosVector(String encoded, boolean is3D, int precision) {
12+
if (encoded == null) {
13+
return null;
14+
}
15+
MapPosVector poly = new MapPosVector();
16+
int index = 0;
17+
int len = encoded.length();
18+
int lat = 0, lng = 0, ele = 0;
19+
int factor = (int) Math.pow(10, precision);
20+
while (index < len) {
21+
// latitude
22+
int b, shift = 0, result = 0;
23+
do {
24+
b = encoded.charAt(index++) - 63;
25+
result |= (b & 0x1f) << shift;
26+
shift += 5;
27+
} while (b >= 0x20);
28+
int deltaLatitude = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
29+
lat += deltaLatitude;
30+
31+
// longitude
32+
shift = 0;
33+
result = 0;
34+
do {
35+
b = encoded.charAt(index++) - 63;
36+
result |= (b & 0x1f) << shift;
37+
shift += 5;
38+
} while (b >= 0x20);
39+
int deltaLongitude = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
40+
lng += deltaLongitude;
41+
42+
if (is3D) {
43+
// elevation
44+
shift = 0;
45+
result = 0;
46+
do {
47+
b = encoded.charAt(index++) - 63;
48+
result |= (b & 0x1f) << shift;
49+
shift += 5;
50+
} while (b >= 0x20);
51+
int deltaElevation = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
52+
ele += deltaElevation;
53+
poly.add(new MapPos((double) lng / factor, (double) lat / factor, (double) ele / 100));
54+
} else {
55+
poly.add(new MapPos((double) lng / factor, (double) lat / factor));
56+
}
57+
}
58+
return poly;
59+
60+
}
61+
62+
private static void encodeNumber(StringBuilder sb, int num) {
63+
num = num << 1;
64+
if (num < 0) {
65+
num = ~num;
66+
}
67+
while (num >= 0x20) {
68+
int nextValue = (0x20 | (num & 0x1f)) + 63;
69+
sb.append((char) (nextValue));
70+
num >>= 5;
71+
}
72+
num += 63;
73+
sb.append((char) (num));
74+
}
75+
76+
public static String encodeMapPosVector(MapPosVector poly, boolean includeElevation) {
77+
return encodeMapPosVector(poly, includeElevation, 5);
78+
}
79+
80+
static public String encodeMapPosVector(MapPosVector coordinates, boolean includeElevation, int precision) {
81+
long size = coordinates.size();
82+
StringBuilder sb = new StringBuilder(Math.max(20, (int) coordinates.size() * 3));
83+
int prevLat = 0;
84+
int prevLon = 0;
85+
int prevEle = 0;
86+
int factor = (int) Math.pow(10, precision);
87+
MapPos pos = null;
88+
for (int i = 0; i < size; i++) {
89+
pos = coordinates.get(i);
90+
int num = (int) Math.floor(pos.getY() * factor);
91+
encodeNumber(sb, num - prevLat);
92+
prevLat = num;
93+
num = (int) Math.floor(pos.getX() * factor);
94+
encodeNumber(sb, num - prevLon);
95+
prevLon = num;
96+
if (includeElevation) {
97+
num = (int) Math.floor(pos.getZ() * 100);
98+
encodeNumber(sb, num - prevEle);
99+
prevEle = num;
100+
}
101+
}
102+
return sb.toString();
103+
}
104+
105+
static public double arcHav(double x) {
106+
return 2 * Math.asin(Math.sqrt(x));
107+
}
108+
109+
static public double hav(double x) {
110+
final double sinHalf = Math.sin(x * 0.5f);
111+
return sinHalf * sinHalf;
112+
}
113+
114+
static public double havDistance(double lat1, double lat2, double dLng) {
115+
return hav(lat1 - lat2) + hav(dLng) * Math.cos(lat1) * Math.cos(lat2);
116+
}
117+
118+
static public double wrap(double n, double min, double max) {
119+
return n >= min && n < max ? n : ((n - min) % (max - min)) + min;
120+
}
121+
122+
static public double clamp(double x, double low, double high) {
123+
return x < low ? low : x > high ? high : x;
124+
}
125+
126+
static public double mercator(double lat) {
127+
return Math.log(Math.tan(lat * 0.5 + Math.PI / 4f));
128+
}
129+
130+
static public double inverseMercator(double y) {
131+
return 2 * Math.atan(Math.exp(y)) - Math.PI / 2f;
132+
}
133+
134+
static public double sinSumFromHav(double x, double y) {
135+
double a = Math.sqrt(x * (1 - x));
136+
double b = Math.sqrt(y * (1 - y));
137+
return 2 * (a + b - 2 * (a * y + b * x));
138+
}
139+
140+
static public double sinFromHav(double h) {
141+
return 2 * Math.sqrt(h * (1 - h));
142+
}
143+
144+
static public double havFromSin(double x) {
145+
double x2 = x * x;
146+
return (x2 / (1 + Math.sqrt(1 - x2))) * 0.5;
147+
}
148+
149+
static public double sinDeltaBearing(double lat1, double lng1, double lat2, double lng2, double lat3, double lng3) {
150+
double sinLat1 = Math.sin(lat1);
151+
double cosLat2 = Math.cos(lat2);
152+
double cosLat3 = Math.cos(lat3);
153+
double lat31 = lat3 - lat1;
154+
double lng31 = lng3 - lng1;
155+
double lat21 = lat2 - lat1;
156+
double lng21 = lng2 - lng1;
157+
double a = Math.sin(lng31) * cosLat3;
158+
double c = Math.sin(lng21) * cosLat2;
159+
double b = Math.sin(lat31) + 2 * sinLat1 * cosLat3 * hav(lng31);
160+
double d = Math.sin(lat21) + 2 * sinLat1 * cosLat2 * hav(lng21);
161+
double denom = (a * a + b * b) * (c * c + d * d);
162+
return denom <= 0 ? 1 : (a * d - b * c) / Math.sqrt(denom);
163+
}
164+
165+
static public double toRadians(double value) {
166+
return value * TO_RAD;
167+
}
168+
169+
static public double distanceRadians(double lat1, double lng1, double lat2, double lng2) {
170+
return arcHav(havDistance(lat1, lat2, lng1 - lng2));
171+
}
172+
173+
static public double computeAngleBetween(MapPos from, MapPos to) {
174+
return distanceRadians(toRadians(from.getY()), toRadians(from.getX()), toRadians(to.getY()),
175+
toRadians(to.getX()));
176+
}
177+
178+
static public double computeDistanceBetween(MapPos from, MapPos to) {
179+
return computeAngleBetween(from, to) * EARTH_RADIUS;
180+
}
181+
182+
static public double distanceToEnd(int index, MapPosVector poly) {
183+
int result = 0;
184+
long size = poly.size();
185+
MapPos last = null;
186+
MapPos element;
187+
for (int i = index; i < size; i++) {
188+
element = poly.get(i);
189+
if (last != null) {
190+
result += computeDistanceBetween(last, element);
191+
}
192+
last = element;
193+
}
194+
return result;
195+
}
196+
197+
static public boolean isOnSegmentGC(double lat1, double lng1, double lat2, double lng2, double lat3, double lng3,
198+
double havTolerance) {
199+
double havDist13 = havDistance(lat1, lat3, lng1 - lng3);
200+
if (havDist13 <= havTolerance) {
201+
return true;
202+
}
203+
double havDist23 = havDistance(lat2, lat3, lng2 - lng3);
204+
if (havDist23 <= havTolerance) {
205+
return true;
206+
}
207+
double sinBearing = sinDeltaBearing(lat1, lng1, lat2, lng2, lat3, lng3);
208+
double sinDist13 = sinFromHav(havDist13);
209+
double havCrossTrack = havFromSin(sinDist13 * sinBearing);
210+
if (havCrossTrack > havTolerance) {
211+
return false;
212+
}
213+
double havDist12 = havDistance(lat1, lat2, lng1 - lng2);
214+
double term = havDist12 + havCrossTrack * (1 - 2 * havDist12);
215+
if (havDist13 > term || havDist23 > term) {
216+
return false;
217+
}
218+
if (havDist12 < 0.74) {
219+
return true;
220+
}
221+
double cosCrossTrack = 1 - 2 * havCrossTrack;
222+
double havAlongTrack13 = (havDist13 - havCrossTrack) / cosCrossTrack;
223+
double havAlongTrack23 = (havDist23 - havCrossTrack) / cosCrossTrack;
224+
double sinSumAlongTrack = sinSumFromHav(havAlongTrack13, havAlongTrack23);
225+
return sinSumAlongTrack > 0; // Compare with half-circle == PI using sign of sin().
226+
}
227+
228+
static public long isLocationOnPath(MapPos point, MapPosVector poly) {
229+
return isLocationOnPath(point, poly, false, true, 0.1);
230+
}
231+
232+
static public long isLocationOnPath(MapPos point, MapPosVector poly, boolean closed) {
233+
return isLocationOnPath(point, poly, closed, true, 0.1);
234+
}
235+
236+
static public long isLocationOnPath(MapPos point, MapPosVector poly, boolean closed, boolean geodesic) {
237+
return isLocationOnPath(point, poly, closed, geodesic, 0.1);
238+
}
239+
240+
static public long isLocationOnPath(MapPos point, MapPosVector poly, boolean closed, boolean geodesic,
241+
double toleranceEarth) {
242+
long size = poly.size();
243+
if (size == 0) {
244+
return -1;
245+
}
246+
double tolerance = toleranceEarth / EARTH_RADIUS;
247+
double havTolerance = hav(tolerance);
248+
double lat3 = toRadians(point.getY());
249+
double lng3 = toRadians(point.getX());
250+
MapPos prev = poly.get(closed ? (int) (size - 1) : 0);
251+
double lat1 = toRadians(prev.getY());
252+
double lng1 = toRadians(prev.getX());
253+
if (geodesic) {
254+
for (int index = 0; index < size; index++) {
255+
MapPos point2 = poly.get(index);
256+
257+
double lat2 = toRadians(point2.getY());
258+
double lng2 = toRadians(point2.getX());
259+
if (isOnSegmentGC(lat1, lng1, lat2, lng2, lat3, lng3, havTolerance)) {
260+
return index;
261+
}
262+
lat1 = lat2;
263+
lng1 = lng2;
264+
}
265+
} else {
266+
// We project the points to mercator space, where the Rhumb segment is a
267+
// straight line,
268+
// and compute the geodesic distance between point3 and the closest point on the
269+
// segment. This method is an approximation, because it uses "closest" in
270+
// mercator
271+
// space which is not "closest" on the sphere -- but the error is small because
272+
// "tolerance" is small.
273+
double minAcceptable = lat3 - tolerance;
274+
double maxAcceptable = lat3 + tolerance;
275+
double y1 = mercator(lat1);
276+
double y3 = mercator(lat3);
277+
double[] xTry = {};
278+
for (int index = 0; index < size; index++) {
279+
MapPos point2 = poly.get(index);
280+
double lat2 = toRadians(point2.getY());
281+
double y2 = mercator(lat2);
282+
double lng2 = toRadians(point2.getX());
283+
if (Math.max(lat1, lat2) >= minAcceptable && Math.min(lat1, lat2) <= maxAcceptable) {
284+
// We offset longitudes by -lng1; the implicit x1 is 0.
285+
double x2 = wrap(lng2 - lng1, -Math.PI, Math.PI);
286+
double x3Base = wrap(lng3 - lng1, -Math.PI, Math.PI);
287+
xTry[0] = x3Base;
288+
// Also explore wrapping of x3Base around the world in both directions.
289+
xTry[1] = x3Base + 2 * Math.PI;
290+
xTry[2] = x3Base - 2 * Math.PI;
291+
for (int index2 = 0; index2 < xTry.length; index2++) {
292+
double x3 = xTry[index2];
293+
double dy = y2 - y1;
294+
double len2 = x2 * x2 + dy * dy;
295+
double t = len2 <= 0 ? 0 : clamp((x3 * x2 + (y3 - y1) * dy) / len2, 0, 1);
296+
double xClosest = t * x2;
297+
double yClosest = y1 + t * dy;
298+
double latClosest = inverseMercator(yClosest);
299+
double havDist = havDistance(lat3, latClosest, x3 - xClosest);
300+
if (havDist < havTolerance) {
301+
return index;
302+
}
303+
}
304+
}
305+
lat1 = lat2;
306+
lng1 = lng2;
307+
y1 = y2;
308+
}
309+
}
310+
return -1;
311+
}
312+
}

src/core/index.android.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,10 @@ export class MapPosVector<T = DefaultLatLonKeys> extends NativeVector<com.carto.
208208
return this.native.add(toNativeMapPos(position));
209209
}
210210

211+
getPos(index: number) {
212+
return fromNativeMapPos<T>(this.get(index));
213+
}
214+
211215
toArray() {
212216
const result: GenericMapPos<T>[] = [];
213217
for (let i = 0; i < this.size(); i++) {

src/core/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export abstract class NativeVector<T> {
5151
public set(index: number, value: T);
5252
}
5353
export class MapPosVector<T = DefaultLatLonKeys> extends NativeVector<NMapPos> {
54+
getPos(index: number): GenericMapPos<T>;
5455
toArray(): GenericMapPos<T>[];
5556
}
5657
export class MapPosVectorVector<T = DefaultLatLonKeys> extends NativeVector<NativeVector<GenericMapPos<T>>> {}

src/core/index.ios.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,10 @@ export class MapPosVector<T = DefaultLatLonKeys> extends NativeVector<NTMapPos>
179179
return this.native.add(toNativeMapPos(position));
180180
}
181181

182+
getPos(index: number) {
183+
return fromNativeMapPos<T>(this.get(index));
184+
}
185+
182186
toArray() {
183187
const result: GenericMapPos<T>[] = [];
184188
for (let i = 0; i < this.size(); i++) {

src/index.android.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,14 @@ export function nativeImageProperty(...args) {
8787
);
8888
}
8989

90-
export function mapPosVectorFromArgs<T = DefaultLatLonKeys>(positions: MapPosVector<T> | GenericMapPos<T>[], ignoreAltitude = true) {
91-
let nativePoses: com.carto.core.MapPosVector;
90+
export function mapPosVectorFromArgs<T = DefaultLatLonKeys>(positions: MapPosVector<T> | GenericMapPos<T>[] | com.carto.core.MapPosVector, ignoreAltitude = true) {
9291
if (!positions) {
9392
return null;
9493
}
94+
let nativePoses: com.carto.core.MapPosVector;
9595
if (typeof (positions as any).getNative === 'function') {
9696
nativePoses = (positions as MapPosVector<T>).getNative();
97-
} else {
97+
} else if (!(positions instanceof com.carto.core.MapPosVector)) {
9898
const arrayPoses = positions as GenericMapPos<T>[];
9999
nativePoses = new com.carto.core.MapPosVector();
100100
// if (projection) {

src/index.ios.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,14 @@ export function nativeImageProperty(...args) {
116116
// });
117117
// }
118118

119-
export function mapPosVectorFromArgs(positions: MapPosVector | MapPos[], ignoreAltitude = true) {
119+
export function mapPosVectorFromArgs(positions: MapPosVector | MapPos[] | NTMapPosVector, ignoreAltitude = true) {
120+
if (!positions) {
121+
return null;
122+
}
120123
let nativePoses: NTMapPosVector;
121124
if (typeof (positions as any).getNative === 'function') {
122125
nativePoses = (positions as MapPosVector).getNative();
123-
} else {
126+
} else if (!(positions instanceof NTMapPosVector)) {
124127
const arrayPoses = positions as MapPos[];
125128
nativePoses = NTMapPosVector.alloc().init();
126129
// if (projection) {

0 commit comments

Comments
 (0)