From c5a57536ab78d5f6872b1e8ce79b4a8cd5c0beda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20Kn=C3=B6chel?= Date: Tue, 30 Jan 2024 10:30:54 +0100 Subject: [PATCH 1/6] feat(ios): add cutout circle API --- apidoc/Map.yml | 30 ++++++++++++++++++++- apidoc/View.yml | 13 ++++++++++ ios/Classes/TiCutoutCircle.h | 12 +++++++++ ios/Classes/TiCutoutCircle.m | 12 +++++++++ ios/Classes/TiMapPolygonProxy.m | 26 +++---------------- ios/Classes/TiMapPolylineProxy.m | 24 ++--------------- ios/Classes/TiMapUtils.h | 4 +++ ios/Classes/TiMapUtils.m | 43 +++++++++++++++++++++++++++++++ ios/Classes/TiMapView.m | 17 ++++++++++-- ios/Classes/TiMapViewProxy.m | 35 +++++++++++++++++++++++++ ios/manifest | 2 +- ios/map.xcodeproj/project.pbxproj | 8 ++++++ 12 files changed, 177 insertions(+), 49 deletions(-) create mode 100644 ios/Classes/TiCutoutCircle.h create mode 100644 ios/Classes/TiCutoutCircle.m diff --git a/apidoc/Map.yml b/apidoc/Map.yml index 3b980deb..37b783d1 100644 --- a/apidoc/Map.yml +++ b/apidoc/Map.yml @@ -817,4 +817,32 @@ properties: - - - - type: Array \ No newline at end of file + type: Array + +--- +name: CutoutCircleConfiguration +summary: Options to configure a cutout circle (iOS only right now). +properties: + - name: longitude + summary: Longitude value of the map point, in decimal degrees. + type: Number + - name: latitude + summary: Latitude value of the map point, in decimal degrees. + type: Number + - name: radius + summary: The radius of the circle in meters. + type: Number + - name: overlayColor + summary: The color of the overlay (not the circle). + type: String + - name: strokeColor + summary: | + Color to use for the the polyline, as a color name or hex triplet. + description: | + For information about color values, see the "Colors" section of . + type: String + default: black + - name: strokeWidth + summary: Line width in pixels to use when drawing the polyline. + type: Number + default: 10 diff --git a/apidoc/View.yml b/apidoc/View.yml index b0b46440..6fb54fa1 100644 --- a/apidoc/View.yml +++ b/apidoc/View.yml @@ -280,6 +280,19 @@ methods: - name: removeAllPolylines summary: Remove all polylines from the map. + - name: addCutoutCircle + summary: Adds a new cutout circle to the map. + description: | + A cutout circle represents a special polygon that spans over the + whole world and has a cutout in the shape of a circle. The radius + of the shape, the overlay color and stroke properties can be configured. + exclude-platforms: [android] + since: { iphone: "7.4.0", ipad: "7.4.0", macos: "7.4.0" } + parameters: + - name: circleConfiguration + summary: A object. + type: Modules.Map.CutoutCircleConfiguration + - name: addCircle summary: Adds a new circle to the map. parameters: diff --git a/ios/Classes/TiCutoutCircle.h b/ios/Classes/TiCutoutCircle.h new file mode 100644 index 00000000..a4ced732 --- /dev/null +++ b/ios/Classes/TiCutoutCircle.h @@ -0,0 +1,12 @@ +// +// TiCutoutCircle.h +// map +// +// Created by Hans Knöchel on 30.01.24. +// + +#import + +@interface TiCutoutCircle : MKPolygon + +@end diff --git a/ios/Classes/TiCutoutCircle.m b/ios/Classes/TiCutoutCircle.m new file mode 100644 index 00000000..2730b527 --- /dev/null +++ b/ios/Classes/TiCutoutCircle.m @@ -0,0 +1,12 @@ +// +// TiCutoutCircle.m +// map +// +// Created by Hans Knöchel on 30.01.24. +// + +#import "TiCutoutCircle.h" + +@implementation TiCutoutCircle + +@end diff --git a/ios/Classes/TiMapPolygonProxy.m b/ios/Classes/TiMapPolygonProxy.m index 975c866d..233a6f62 100644 --- a/ios/Classes/TiMapPolygonProxy.m +++ b/ios/Classes/TiMapPolygonProxy.m @@ -6,6 +6,7 @@ */ #import "TiMapPolygonProxy.h" +#import "TiMapUtils.h" @implementation TiMapPolygonProxy @@ -46,7 +47,7 @@ - (void)setupPolygon for (int i = 0; i < [points count]; i++) { id locObj = [points objectAtIndex:i]; - CLLocationCoordinate2D coord = [self processLocation:locObj]; + CLLocationCoordinate2D coord = [TiMapUtils processLocation:locObj]; coordArray[i] = coord; } @@ -61,7 +62,7 @@ - (void)setupPolygon for (int j = 0; j < [holeObj count]; ++j) { id obj = [holeObj objectAtIndex:j]; - CLLocationCoordinate2D coord = [self processLocation:obj]; + CLLocationCoordinate2D coord = [TiMapUtils processLocation:obj]; hole[j] = coord; } @@ -83,27 +84,6 @@ - (void)setupPolygon [self applyStrokeWidth]; } -// A location can either be a an array of longitude, latitude pairings or -// an array of longitude, latitude objects. -// e.g. [ [123.33, 34.44], [100.39, 78.23], etc. ] -// [ {longitude: 123.33, latitude, 34.44}, {longitude: 100.39, latitude: 78.23}, etc. ] -- (CLLocationCoordinate2D)processLocation:(id)locObj -{ - if ([locObj isKindOfClass:[NSDictionary class]]) { - CLLocationDegrees lat = [TiUtils doubleValue:[locObj objectForKey:@"latitude"]]; - CLLocationDegrees lon = [TiUtils doubleValue:[locObj objectForKey:@"longitude"]]; - - return CLLocationCoordinate2DMake(lat, lon); - } else if ([locObj isKindOfClass:[NSArray class]]) { - CLLocationDegrees lat = [TiUtils doubleValue:[locObj objectAtIndex:1]]; - CLLocationDegrees lon = [TiUtils doubleValue:[locObj objectAtIndex:0]]; - - return CLLocationCoordinate2DMake(lat, lon); - } - - return kCLLocationCoordinate2DInvalid; -} - - (void)applyFillColor { if (polygonRenderer != nil) { diff --git a/ios/Classes/TiMapPolylineProxy.m b/ios/Classes/TiMapPolylineProxy.m index 611b150d..7a7f936d 100644 --- a/ios/Classes/TiMapPolylineProxy.m +++ b/ios/Classes/TiMapPolylineProxy.m @@ -7,6 +7,7 @@ #import "TiMapPolylineProxy.h" #import "TiMapConstants.h" +#import "TiMapUtils.h" @implementation TiMapPolylineProxy @@ -45,7 +46,7 @@ - (void)setupPolyline for (int i = 0; i < [points count]; i++) { id locObj = [points objectAtIndex:i]; - CLLocationCoordinate2D coord = [self processLocation:locObj]; + CLLocationCoordinate2D coord = [TiMapUtils processLocation:locObj]; coordArray[i] = coord; } @@ -58,27 +59,6 @@ - (void)setupPolyline [self applyStrokePattern]; } -// A location can either be a an array of longitude, latitude pairings or -// an array of longitude, latitude objects. -// e.g. [ [123.33, 34.44], [100.39, 78.23], etc. ] -// [ {longitude: 123.33, latitude, 34.44}, {longitude: 100.39, latitude: 78.23}, etc. ] -- (CLLocationCoordinate2D)processLocation:(id)locObj -{ - if ([locObj isKindOfClass:[NSDictionary class]]) { - CLLocationDegrees lat = [TiUtils doubleValue:[locObj objectForKey:@"latitude"]]; - CLLocationDegrees lon = [TiUtils doubleValue:[locObj objectForKey:@"longitude"]]; - - return CLLocationCoordinate2DMake(lat, lon); - } else if ([locObj isKindOfClass:[NSArray class]]) { - CLLocationDegrees lat = [TiUtils doubleValue:[locObj objectAtIndex:1]]; - CLLocationDegrees lon = [TiUtils doubleValue:[locObj objectAtIndex:0]]; - - return CLLocationCoordinate2DMake(lat, lon); - } - - return kCLLocationCoordinate2DInvalid; -} - - (void)applyStrokeColor { if (polylineRenderer != nil) { diff --git a/ios/Classes/TiMapUtils.h b/ios/Classes/TiMapUtils.h index ece8353c..c378e82e 100644 --- a/ios/Classes/TiMapUtils.h +++ b/ios/Classes/TiMapUtils.h @@ -17,4 +17,8 @@ + (MKLocalSearchCompleterResultType)mappedResultTypes:(NSArray *)inputResultTypes; ++ (NSArray *)makeCircleCoordinates:(CLLocationCoordinate2D)coordinate withRadius:(double)radius andTolerance:(double)tolerance; + ++ (CLLocationCoordinate2D)processLocation:(id)locObj; + @end diff --git a/ios/Classes/TiMapUtils.m b/ios/Classes/TiMapUtils.m index 85aec6fc..a985c738 100644 --- a/ios/Classes/TiMapUtils.m +++ b/ios/Classes/TiMapUtils.m @@ -68,4 +68,47 @@ + (MKLocalSearchCompleterResultType)mappedResultTypes:(NSArray *)inp return resultTypes; } ++ (NSArray *)makeCircleCoordinates:(CLLocationCoordinate2D)coordinate withRadius:(double)radius andTolerance:(double)tolerance +{ + NSMutableArray *coordinates = [[NSMutableArray alloc] init]; + double latRadian = coordinate.latitude * M_PI / 180.0; + double lngRadian = coordinate.longitude * M_PI / 180.0; + double distance = (radius / 1000.0) / 6371.0; // kms + + for (double angle = 0; angle < 360.0; angle += tolerance) { + double bearing = angle * M_PI / 180.0; + + double lat2 = asin(sin(latRadian) * cos(distance) + cos(latRadian) * sin(distance) * cos(bearing)); + double lon2 = lngRadian + atan2(sin(bearing) * sin(distance) * cos(latRadian), cos(distance) - sin(latRadian) * sin(lat2)); + lon2 = fmod(lon2 + 3 * M_PI, 2 * M_PI) - M_PI; // normalize to -180..+180º + + NSNumber *latitude = @(lat2 * (180.0 / M_PI)); + NSNumber *longitude = @(lon2 * (180.0 / M_PI)); + [coordinates addObject:@{ @"latitude" : latitude, @"longitude" : longitude }]; + } + + return coordinates; +} + +// A location can either be a an array of longitude, latitude pairings or +// an array of longitude, latitude objects. +// e.g. [ [123.33, 34.44], [100.39, 78.23], etc. ] +// [ {longitude: 123.33, latitude, 34.44}, {longitude: 100.39, latitude: 78.23}, etc. ] ++ (CLLocationCoordinate2D)processLocation:(id)locObj +{ + if ([locObj isKindOfClass:[NSDictionary class]]) { + CLLocationDegrees lat = [TiUtils doubleValue:[locObj objectForKey:@"latitude"]]; + CLLocationDegrees lon = [TiUtils doubleValue:[locObj objectForKey:@"longitude"]]; + + return CLLocationCoordinate2DMake(lat, lon); + } else if ([locObj isKindOfClass:[NSArray class]]) { + CLLocationDegrees lat = [TiUtils doubleValue:[locObj objectAtIndex:1]]; + CLLocationDegrees lon = [TiUtils doubleValue:[locObj objectAtIndex:0]]; + + return CLLocationCoordinate2DMake(lat, lon); + } + + return kCLLocationCoordinate2DInvalid; +} + @end diff --git a/ios/Classes/TiMapView.m b/ios/Classes/TiMapView.m index 610680e3..f89fda39 100644 --- a/ios/Classes/TiMapView.m +++ b/ios/Classes/TiMapView.m @@ -6,6 +6,7 @@ */ #import "TiMapView.h" +#import "TiCutoutCircle.h" #import "TiMapAnnotationProxy.h" #import "TiMapCircleProxy.h" #import "TiMapCustomAnnotationView.h" @@ -977,7 +978,6 @@ - (void)addOverlay:(MKPolyline *)polyline level:(MKOverlayLevel)level #pragma mark Delegates -// Delegate for >= iOS 8 - (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status { if ((status == kCLAuthorizationStatusAuthorizedWhenInUse) || (status == kCLAuthorizationStatusAuthorizedAlways)) { @@ -985,9 +985,22 @@ - (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatu } } -// Delegate for >= iOS 7 - (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id)overlay { + if ([overlay isKindOfClass:[TiCutoutCircle class]]) { + NSDictionary *circleConfiguration = [[self proxy] valueForKey:@"cutoutCircle"]; + UIColor *overlayColor = [TiUtils colorValue:@"overlayColor" properties:circleConfiguration def:[TiColor colorNamed:@"black"]].color; + UIColor *strokeColor = [TiUtils colorValue:@"strokeColor" properties:circleConfiguration def:[TiColor colorNamed:@"black"]].color; + CGFloat lineWidth = [TiUtils floatValue:@"strokeWidth" properties:circleConfiguration def:1.0]; + + MKPolygonRenderer *renderer = [[MKPolygonRenderer alloc] initWithOverlay:overlay]; + renderer.lineWidth = lineWidth; + renderer.strokeColor = strokeColor; + renderer.fillColor = overlayColor; + + return renderer; + } + return (MKOverlayRenderer *)CFDictionaryGetValue(mapObjects2View, overlay); } diff --git a/ios/Classes/TiMapViewProxy.m b/ios/Classes/TiMapViewProxy.m index c64dc204..dda55860 100644 --- a/ios/Classes/TiMapViewProxy.m +++ b/ios/Classes/TiMapViewProxy.m @@ -5,6 +5,7 @@ * Please see the LICENSE included with this distribution for details. */ #import "TiMapViewProxy.h" +#import "TiCutoutCircle.h" #import "TiMapAnnotationProxy.h" #import "TiMapCircleProxy.h" #import "TiMapImageOverlayProxy.h" @@ -291,6 +292,40 @@ - (void)addAnnotations:(id)arg } } +- (void)addCutoutCircle:(id)arg +{ + ENSURE_SINGLE_ARG(arg, NSDictionary); + + [self replaceValue:arg forKey:@"cutoutCircle" notification:NO]; + + CGFloat latitude = [TiUtils floatValue:@"latitude" properties:arg]; + CGFloat longitude = [TiUtils floatValue:@"longitude" properties:arg]; + double radius = [TiUtils doubleValue:@"radius" properties:arg]; + double tolerance = [TiUtils doubleValue:@"tolerance" properties:arg def:3.0]; + + CLLocationCoordinate2D WORLD_COORDINATES[6]; + WORLD_COORDINATES[0] = CLLocationCoordinate2DMake(90, 0); + WORLD_COORDINATES[1] = CLLocationCoordinate2DMake(90, 180); + WORLD_COORDINATES[2] = CLLocationCoordinate2DMake(-90, 180); + WORLD_COORDINATES[3] = CLLocationCoordinate2DMake(-90, 0); + WORLD_COORDINATES[4] = CLLocationCoordinate2DMake(-90, -180); + WORLD_COORDINATES[5] = CLLocationCoordinate2DMake(90, -180); + + CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(latitude, longitude); + NSArray *circleCoordinates = [TiMapUtils makeCircleCoordinates:coordinate withRadius:radius andTolerance:tolerance]; + CLLocationCoordinate2D *circleCoordinatesNative = malloc(sizeof(CLLocationCoordinate2D) * [circleCoordinates count]); + + for (int i = 0; i < [circleCoordinates count]; ++i) { + CLLocationCoordinate2D coordinate = [TiMapUtils processLocation:[circleCoordinates objectAtIndex:i]]; + circleCoordinatesNative[i] = coordinate; + } + + MKPolygon *polygon1 = [MKPolygon polygonWithCoordinates:circleCoordinatesNative count:circleCoordinates.count]; + TiCutoutCircle *polygon2 = [TiCutoutCircle polygonWithCoordinates:WORLD_COORDINATES count:6 interiorPolygons:@[ polygon1 ]]; + + [[(TiMapView *)[self view] map] addOverlay:polygon2]; +} + - (void)setAnnotations:(id)arg { ENSURE_TYPE(arg, NSArray); diff --git a/ios/manifest b/ios/manifest index 12ef5eb2..d82b63ab 100644 --- a/ios/manifest +++ b/ios/manifest @@ -3,7 +3,7 @@ # during compilation, packaging, distribution, etc. # -version: 7.3.1 +version: 7.4.0 apiversion: 2 architectures: arm64 x86_64 description: External version of Map module diff --git a/ios/map.xcodeproj/project.pbxproj b/ios/map.xcodeproj/project.pbxproj index a6d15353..a9a788fa 100644 --- a/ios/map.xcodeproj/project.pbxproj +++ b/ios/map.xcodeproj/project.pbxproj @@ -37,6 +37,8 @@ 27A609F019F711B700CA150A /* TiMapPolylineProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 27A609EF19F711B700CA150A /* TiMapPolylineProxy.m */; }; 3A155BE82A9B35C000247646 /* TiMapFeatureAnnotationProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A155BE62A9B35C000247646 /* TiMapFeatureAnnotationProxy.h */; }; 3A155BE92A9B35C000247646 /* TiMapFeatureAnnotationProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A155BE72A9B35C000247646 /* TiMapFeatureAnnotationProxy.m */; }; + 3A699AC92B68CB0E00938935 /* TiCutoutCircle.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A699AC72B68CB0E00938935 /* TiCutoutCircle.h */; }; + 3A699ACA2B68CB0E00938935 /* TiCutoutCircle.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A699AC82B68CB0E00938935 /* TiCutoutCircle.m */; }; 815136F31D355BF5009E3E2C /* TiMapSnapshotterProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 815136F11D355BF5009E3E2C /* TiMapSnapshotterProxy.h */; }; 815136F41D355BF5009E3E2C /* TiMapSnapshotterProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 815136F21D355BF5009E3E2C /* TiMapSnapshotterProxy.m */; }; AA747D9F0F9514B9006C5449 /* TiMap_Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = AA747D9E0F9514B9006C5449 /* TiMap_Prefix.pch */; }; @@ -98,6 +100,8 @@ 27A609EF19F711B700CA150A /* TiMapPolylineProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TiMapPolylineProxy.m; path = Classes/TiMapPolylineProxy.m; sourceTree = ""; }; 3A155BE62A9B35C000247646 /* TiMapFeatureAnnotationProxy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TiMapFeatureAnnotationProxy.h; path = Classes/TiMapFeatureAnnotationProxy.h; sourceTree = ""; }; 3A155BE72A9B35C000247646 /* TiMapFeatureAnnotationProxy.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = TiMapFeatureAnnotationProxy.m; path = Classes/TiMapFeatureAnnotationProxy.m; sourceTree = ""; }; + 3A699AC72B68CB0E00938935 /* TiCutoutCircle.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TiCutoutCircle.h; path = Classes/TiCutoutCircle.h; sourceTree = ""; }; + 3A699AC82B68CB0E00938935 /* TiCutoutCircle.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = TiCutoutCircle.m; path = Classes/TiCutoutCircle.m; sourceTree = ""; }; 815136F11D355BF5009E3E2C /* TiMapSnapshotterProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TiMapSnapshotterProxy.h; path = Classes/TiMapSnapshotterProxy.h; sourceTree = ""; }; 815136F21D355BF5009E3E2C /* TiMapSnapshotterProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TiMapSnapshotterProxy.m; path = Classes/TiMapSnapshotterProxy.m; sourceTree = ""; }; AA747D9E0F9514B9006C5449 /* TiMap_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TiMap_Prefix.pch; sourceTree = SOURCE_ROOT; }; @@ -265,6 +269,8 @@ DB0CCA022010841A00597F24 /* Pattern */, DB07496B1E081A36009D8A71 /* Polygon */, DB07496A1E081A23009D8A71 /* Polyline */, + 3A699AC72B68CB0E00938935 /* TiCutoutCircle.h */, + 3A699AC82B68CB0E00938935 /* TiCutoutCircle.m */, ); name = Overlays; sourceTree = ""; @@ -336,6 +342,7 @@ buildActionMask = 2147483647; files = ( 194D001C2047DB4A002150BD /* TiMapImageOverlayProxy.h in Headers */, + 3A699AC92B68CB0E00938935 /* TiCutoutCircle.h in Headers */, AA747D9F0F9514B9006C5449 /* TiMap_Prefix.pch in Headers */, CE92EC2217DF8B020082E943 /* TiMKOverlayPathUniversal.h in Headers */, 24DD6CF91134B3F500162E58 /* TiMapModule.h in Headers */, @@ -430,6 +437,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 3A699ACA2B68CB0E00938935 /* TiCutoutCircle.m in Sources */, 194D00212047DDED002150BD /* TiMapImageOverlayRenderer.m in Sources */, 24DD6CFA1134B3F500162E58 /* TiMapModule.m in Sources */, 194D001D2047DB4A002150BD /* TiMapImageOverlayProxy.m in Sources */, From f54bb2c122ee9fc3830c0a2527706e669ad57e62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20Kn=C3=B6chel?= Date: Tue, 30 Jan 2024 10:55:13 +0100 Subject: [PATCH 2/6] fix: fix doc linting issues --- apidoc/View.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apidoc/View.yml b/apidoc/View.yml index 6fb54fa1..bfc291fc 100644 --- a/apidoc/View.yml +++ b/apidoc/View.yml @@ -287,11 +287,11 @@ methods: whole world and has a cutout in the shape of a circle. The radius of the shape, the overlay color and stroke properties can be configured. exclude-platforms: [android] - since: { iphone: "7.4.0", ipad: "7.4.0", macos: "7.4.0" } + since: { iphone: "12.3.0", ipad: "12.3.0", macos: "12.3.0" } parameters: - name: circleConfiguration - summary: A object. - type: Modules.Map.CutoutCircleConfiguration + summary: A object. + type: CutoutCircleConfiguration - name: addCircle summary: Adds a new circle to the map. From 216e7cfe8c5256b5624d0f8abfff9e4312f77e8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20Kn=C3=B6chel?= Date: Tue, 30 Jan 2024 11:01:18 +0100 Subject: [PATCH 3/6] chore: clean up naming, update license header --- ios/Classes/TiCutoutCircle.h | 12 ++++++------ ios/Classes/TiCutoutCircle.m | 12 ++++++------ ios/Classes/TiMapUtils.h | 2 +- ios/Classes/TiMapUtils.m | 2 +- ios/Classes/TiMapViewProxy.m | 15 +++++++++------ ios/map.xcodeproj/project.pbxproj | 4 ++-- 6 files changed, 25 insertions(+), 22 deletions(-) diff --git a/ios/Classes/TiCutoutCircle.h b/ios/Classes/TiCutoutCircle.h index a4ced732..030c321e 100644 --- a/ios/Classes/TiCutoutCircle.h +++ b/ios/Classes/TiCutoutCircle.h @@ -1,9 +1,9 @@ -// -// TiCutoutCircle.h -// map -// -// Created by Hans Knöchel on 30.01.24. -// +/** + * Titanium SDK + * Copyright TiDev, Inc. 04/07/2022-Present. All Rights Reserved. + * Licensed under the terms of the Apache Public License + * Please see the LICENSE included with this distribution for details. + */ #import diff --git a/ios/Classes/TiCutoutCircle.m b/ios/Classes/TiCutoutCircle.m index 2730b527..b1033b36 100644 --- a/ios/Classes/TiCutoutCircle.m +++ b/ios/Classes/TiCutoutCircle.m @@ -1,9 +1,9 @@ -// -// TiCutoutCircle.m -// map -// -// Created by Hans Knöchel on 30.01.24. -// +/** + * Titanium SDK + * Copyright TiDev, Inc. 04/07/2022-Present. All Rights Reserved. + * Licensed under the terms of the Apache Public License + * Please see the LICENSE included with this distribution for details. + */ #import "TiCutoutCircle.h" diff --git a/ios/Classes/TiMapUtils.h b/ios/Classes/TiMapUtils.h index c378e82e..19f553ed 100644 --- a/ios/Classes/TiMapUtils.h +++ b/ios/Classes/TiMapUtils.h @@ -17,7 +17,7 @@ + (MKLocalSearchCompleterResultType)mappedResultTypes:(NSArray *)inputResultTypes; -+ (NSArray *)makeCircleCoordinates:(CLLocationCoordinate2D)coordinate withRadius:(double)radius andTolerance:(double)tolerance; ++ (NSArray *)generateCircleCoordinates:(CLLocationCoordinate2D)coordinate withRadius:(double)radius andTolerance:(double)tolerance; + (CLLocationCoordinate2D)processLocation:(id)locObj; diff --git a/ios/Classes/TiMapUtils.m b/ios/Classes/TiMapUtils.m index a985c738..75a1dd8f 100644 --- a/ios/Classes/TiMapUtils.m +++ b/ios/Classes/TiMapUtils.m @@ -68,7 +68,7 @@ + (MKLocalSearchCompleterResultType)mappedResultTypes:(NSArray *)inp return resultTypes; } -+ (NSArray *)makeCircleCoordinates:(CLLocationCoordinate2D)coordinate withRadius:(double)radius andTolerance:(double)tolerance ++ (NSArray *)generateCircleCoordinates:(CLLocationCoordinate2D)coordinate withRadius:(double)radius andTolerance:(double)tolerance { NSMutableArray *coordinates = [[NSMutableArray alloc] init]; double latRadian = coordinate.latitude * M_PI / 180.0; diff --git a/ios/Classes/TiMapViewProxy.m b/ios/Classes/TiMapViewProxy.m index dda55860..8955d89f 100644 --- a/ios/Classes/TiMapViewProxy.m +++ b/ios/Classes/TiMapViewProxy.m @@ -300,7 +300,7 @@ - (void)addCutoutCircle:(id)arg CGFloat latitude = [TiUtils floatValue:@"latitude" properties:arg]; CGFloat longitude = [TiUtils floatValue:@"longitude" properties:arg]; - double radius = [TiUtils doubleValue:@"radius" properties:arg]; + CGFloat radius = [TiUtils doubleValue:@"radius" properties:arg]; double tolerance = [TiUtils doubleValue:@"tolerance" properties:arg def:3.0]; CLLocationCoordinate2D WORLD_COORDINATES[6]; @@ -312,18 +312,21 @@ - (void)addCutoutCircle:(id)arg WORLD_COORDINATES[5] = CLLocationCoordinate2DMake(90, -180); CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(latitude, longitude); - NSArray *circleCoordinates = [TiMapUtils makeCircleCoordinates:coordinate withRadius:radius andTolerance:tolerance]; + NSArray *circleCoordinates = [TiMapUtils generateCircleCoordinates:coordinate + withRadius:radius + andTolerance:tolerance]; + CLLocationCoordinate2D *circleCoordinatesNative = malloc(sizeof(CLLocationCoordinate2D) * [circleCoordinates count]); - for (int i = 0; i < [circleCoordinates count]; ++i) { + for (NSUInteger i = 0; i < [circleCoordinates count]; ++i) { CLLocationCoordinate2D coordinate = [TiMapUtils processLocation:[circleCoordinates objectAtIndex:i]]; circleCoordinatesNative[i] = coordinate; } - MKPolygon *polygon1 = [MKPolygon polygonWithCoordinates:circleCoordinatesNative count:circleCoordinates.count]; - TiCutoutCircle *polygon2 = [TiCutoutCircle polygonWithCoordinates:WORLD_COORDINATES count:6 interiorPolygons:@[ polygon1 ]]; + MKPolygon *circlePolygon = [MKPolygon polygonWithCoordinates:circleCoordinatesNative count:circleCoordinates.count]; + TiCutoutCircle *cutoutPolygon = [TiCutoutCircle polygonWithCoordinates:WORLD_COORDINATES count:6 interiorPolygons:@[ circlePolygon ]]; - [[(TiMapView *)[self view] map] addOverlay:polygon2]; + [[(TiMapView *)[self view] map] addOverlay:cutoutPolygon]; } - (void)setAnnotations:(id)arg diff --git a/ios/map.xcodeproj/project.pbxproj b/ios/map.xcodeproj/project.pbxproj index a9a788fa..1e91fd63 100644 --- a/ios/map.xcodeproj/project.pbxproj +++ b/ios/map.xcodeproj/project.pbxproj @@ -255,6 +255,8 @@ DB07496D1E081A6A009D8A71 /* Circle */ = { isa = PBXGroup; children = ( + 3A699AC72B68CB0E00938935 /* TiCutoutCircle.h */, + 3A699AC82B68CB0E00938935 /* TiCutoutCircle.m */, 27A609EB19F5B84000CA150A /* TiMapCircleProxy.m */, 27A609ED19F5B85900CA150A /* TiMapCircleProxy.h */, ); @@ -269,8 +271,6 @@ DB0CCA022010841A00597F24 /* Pattern */, DB07496B1E081A36009D8A71 /* Polygon */, DB07496A1E081A23009D8A71 /* Polyline */, - 3A699AC72B68CB0E00938935 /* TiCutoutCircle.h */, - 3A699AC82B68CB0E00938935 /* TiCutoutCircle.m */, ); name = Overlays; sourceTree = ""; From 41b383f6fe76e53fc55a8dc23d0ef15be9368ef7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20Kn=C3=B6chel?= Date: Tue, 30 Jan 2024 11:03:02 +0100 Subject: [PATCH 4/6] chore: guard configuration --- ios/Classes/TiMapView.m | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ios/Classes/TiMapView.m b/ios/Classes/TiMapView.m index f89fda39..2d84732d 100644 --- a/ios/Classes/TiMapView.m +++ b/ios/Classes/TiMapView.m @@ -987,8 +987,10 @@ - (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatu - (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id)overlay { - if ([overlay isKindOfClass:[TiCutoutCircle class]]) { - NSDictionary *circleConfiguration = [[self proxy] valueForKey:@"cutoutCircle"]; + NSDictionary *circleConfiguration = [[self proxy] valueForKey:@"cutoutCircle"]; + + // Configure cutout circles in a special way, as they do not own an own proxy + if ([overlay isKindOfClass:[TiCutoutCircle class]] && circleConfiguration != nil) { UIColor *overlayColor = [TiUtils colorValue:@"overlayColor" properties:circleConfiguration def:[TiColor colorNamed:@"black"]].color; UIColor *strokeColor = [TiUtils colorValue:@"strokeColor" properties:circleConfiguration def:[TiColor colorNamed:@"black"]].color; CGFloat lineWidth = [TiUtils floatValue:@"strokeWidth" properties:circleConfiguration def:1.0]; From 2fa6a077ebdf22faca850ba075487c3ff08d51e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20Kn=C3=B6chel?= Date: Tue, 30 Jan 2024 15:16:54 +0100 Subject: [PATCH 5/6] =?UTF-8?q?feat(ios):=20add=20=E2=80=9CremoveCutoutCir?= =?UTF-8?q?cle=E2=80=9D=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apidoc/View.yml | 5 +++++ ios/Classes/TiMapViewProxy.m | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/apidoc/View.yml b/apidoc/View.yml index bfc291fc..e42c3568 100644 --- a/apidoc/View.yml +++ b/apidoc/View.yml @@ -293,6 +293,11 @@ methods: summary: A object. type: CutoutCircleConfiguration + - name: removeCutoutCircle + summary: Removes a previously added cutout circle. + exclude-platforms: [android] + since: { iphone: "12.3.0", ipad: "12.3.0", macos: "12.3.0" } + - name: addCircle summary: Adds a new circle to the map. parameters: diff --git a/ios/Classes/TiMapViewProxy.m b/ios/Classes/TiMapViewProxy.m index 8955d89f..74ff9b86 100644 --- a/ios/Classes/TiMapViewProxy.m +++ b/ios/Classes/TiMapViewProxy.m @@ -329,6 +329,18 @@ - (void)addCutoutCircle:(id)arg [[(TiMapView *)[self view] map] addOverlay:cutoutPolygon]; } +- (void)removeCutoutCircle:(id)unused +{ + MKMapView *mapView = [(TiMapView *)[self view] map]; + NSArray> *overlays = [mapView overlays]; + + for (id overlay in overlays) { + if ([overlay isKindOfClass:[TiCutoutCircle class]]) { + [mapView removeOverlay:overlay]; + } + } +} + - (void)setAnnotations:(id)arg { ENSURE_TYPE(arg, NSArray); From 54e90a6c85b825118e7bf3a4e4b84092347144f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20Kn=C3=B6chel?= Date: Tue, 30 Jan 2024 22:41:07 +0100 Subject: [PATCH 6/6] feat(android): support android (wip) --- android/manifest | 2 +- android/src/ti/map/MapModule.java | 1 + android/src/ti/map/TiMapUtils.java | 37 +++++++++++++++++++++++++++++ android/src/ti/map/TiUIMapView.java | 31 ++++++++++++++++++++++++ android/src/ti/map/ViewProxy.java | 25 +++++++++++++++++++ apidoc/Map.yml | 2 +- apidoc/View.yml | 3 +-- 7 files changed, 97 insertions(+), 4 deletions(-) diff --git a/android/manifest b/android/manifest index 6c6ce06c..949a175a 100644 --- a/android/manifest +++ b/android/manifest @@ -2,7 +2,7 @@ # this is your module manifest and used by Titanium # during compilation, packaging, distribution, etc. # -version: 5.6.1 +version: 5.7.0 apiversion: 4 architectures: arm64-v8a armeabi-v7a x86 x86_64 description: External version of Map module using native Google Maps library diff --git a/android/src/ti/map/MapModule.java b/android/src/ti/map/MapModule.java index ff21d374..7e9c82c8 100644 --- a/android/src/ti/map/MapModule.java +++ b/android/src/ti/map/MapModule.java @@ -60,6 +60,7 @@ public class MapModule extends KrollModule implements OnMapsSdkInitializedCallba public static final String PROPERTY_STROKE_COLOR = "strokeColor"; public static final String PROPERTY_STROKE_WIDTH = "strokeWidth"; public static final String PROPERTY_FILL_COLOR = "fillColor"; + public static final String PROPERTY_OVERLAY_COLOR = "overlayColor"; public static final String PROPERTY_ZINDEX = "zIndex"; public static final String PROPERTY_POLYGON = "polygon"; public static final String PROPERTY_POLYGONS = "polygons"; diff --git a/android/src/ti/map/TiMapUtils.java b/android/src/ti/map/TiMapUtils.java index 9dd8168a..c6d52782 100644 --- a/android/src/ti/map/TiMapUtils.java +++ b/android/src/ti/map/TiMapUtils.java @@ -53,4 +53,41 @@ public static ArrayList processPoints(Object points) } return locationArray; } + + public static List createOuterBounds() { + float delta = 0.01f; + + return new ArrayList() {{ + add(new LatLng(90 - delta, -180 + delta)); + add(new LatLng(0, -180 + delta)); + add(new LatLng(-90 + delta, -180 + delta)); + add(new LatLng(-90 + delta, 0)); + add(new LatLng(-90 + delta, 180 - delta)); + add(new LatLng(0, 180 - delta)); + add(new LatLng(90 - delta, 180 - delta)); + add(new LatLng(90 - delta, 0)); + add(new LatLng(90 - delta, -180 + delta)); + }}; + } + + public static Iterable createHole(LatLng center, int radius) { + int points = 50; // number of corners of inscribed polygon + + double radiusLatitude = Math.toDegrees(radius / 1000 / 6371.0); + double radiusLongitude = radiusLatitude / Math.cos(Math.toRadians(center.latitude)); + + List result = new ArrayList<>(points); + + double anglePerCircleRegion = 2 * Math.PI / points; + + for (int i = 0; i < points; i++) { + double theta = i * anglePerCircleRegion; + double latitude = center.latitude + (radiusLatitude * Math.sin(theta)); + double longitude = center.longitude + (radiusLongitude * Math.cos(theta)); + + result.add(new LatLng(latitude, longitude)); + } + + return result; + } } diff --git a/android/src/ti/map/TiUIMapView.java b/android/src/ti/map/TiUIMapView.java index f52b68c5..5d53e49d 100644 --- a/android/src/ti/map/TiUIMapView.java +++ b/android/src/ti/map/TiUIMapView.java @@ -7,6 +7,10 @@ package ti.map; +import static ti.map.TiMapUtils.createHole; +import static ti.map.TiMapUtils.createOuterBounds; +import static ti.map.TiMapUtils.parseLocation; + import android.Manifest; import android.app.Activity; import android.content.Context; @@ -34,6 +38,7 @@ import com.google.android.gms.maps.model.LatLngBounds; import com.google.android.gms.maps.model.MapStyleOptions; import com.google.android.gms.maps.model.Marker; +import com.google.android.gms.maps.model.PolygonOptions; import com.google.android.gms.maps.model.TileOverlayOptions; import com.google.maps.android.clustering.Cluster; import com.google.maps.android.clustering.ClusterManager; @@ -894,6 +899,32 @@ public void removeAllPolylines() currentPolylines.clear(); } + /** + * Cutout Circle + */ + protected void addCutoutCircle(KrollDict circleConfig) { + if (map == null) { + return; + } + + Activity currentActivity = TiApplication.getAppCurrentActivity(); + + LatLng center = parseLocation(circleConfig); + int radius = circleConfig.getInt(MapModule.PROPERTY_RADIUS); + float strokeWidth = circleConfig.getDouble(MapModule.PROPERTY_STROKE_WIDTH).floatValue(); + int strokeColor = TiConvert.toColor(circleConfig.getString(MapModule.PROPERTY_STROKE_COLOR), currentActivity); + int overlayColor = TiConvert.toColor(circleConfig.getString(MapModule.PROPERTY_OVERLAY_COLOR), currentActivity); + + PolygonOptions options = new PolygonOptions() + .fillColor(overlayColor) + .addAll(createOuterBounds()) + .addHole(createHole(center, radius)) + .strokeColor(strokeColor) + .strokeWidth(strokeWidth); + + map.addPolygon(options); + } + /** * Circle */ diff --git a/android/src/ti/map/ViewProxy.java b/android/src/ti/map/ViewProxy.java index a09b9faf..45d5dd3d 100644 --- a/android/src/ti/map/ViewProxy.java +++ b/android/src/ti/map/ViewProxy.java @@ -77,6 +77,7 @@ public class ViewProxy extends TiViewProxy implements AnnotationDelegate private static final int MSG_ADD_CIRCLE = MSG_FIRST_ID + 921; private static final int MSG_REMOVE_CIRCLE = MSG_FIRST_ID + 922; private static final int MSG_REMOVE_ALL_CIRCLES = MSG_FIRST_ID + 923; + private static final int MSG_ADD_CUTOUT_CIRCLE = MSG_FIRST_ID + 924; private static final int MSG_ADD_IMAGE_OVERLAY = MSG_FIRST_ID + 931; private static final int MSG_REMOVE_IMAGE_OVERLAY = MSG_FIRST_ID + 932; @@ -289,6 +290,13 @@ public boolean handleMessage(Message msg) return true; } + case MSG_ADD_CUTOUT_CIRCLE: { + result = (AsyncResult) msg.obj; + handleAddCutoutCircle((KrollDict) result.getArg()); + result.setResult(null); + return true; + } + case MSG_REMOVE_CIRCLE: { result = (AsyncResult) msg.obj; handleRemoveCircle((CircleProxy) result.getArg()); @@ -1105,6 +1113,23 @@ public void handleAddCircle(CircleProxy circle) } } + @Kroll.method + public void addCutoutCircle(KrollDict circleConfiguration) + { + handleAddCutoutCircle(circleConfiguration); + } + + public void handleAddCutoutCircle(KrollDict circleConfiguration) + { + TiUIView view = peekView(); + if (view instanceof TiUIMapView) { + TiUIMapView mapView = (TiUIMapView) view; + if (mapView.getMap() != null) { + mapView.addCutoutCircle(circleConfiguration); + } + } + } + public void addPreloadCircle(CircleProxy c) { if (!preloadCircles.contains(c)) { diff --git a/apidoc/Map.yml b/apidoc/Map.yml index 37b783d1..52b3a5aa 100644 --- a/apidoc/Map.yml +++ b/apidoc/Map.yml @@ -821,7 +821,7 @@ properties: --- name: CutoutCircleConfiguration -summary: Options to configure a cutout circle (iOS only right now). +summary: Options to configure a cutout circle. properties: - name: longitude summary: Longitude value of the map point, in decimal degrees. diff --git a/apidoc/View.yml b/apidoc/View.yml index e42c3568..abfb713f 100644 --- a/apidoc/View.yml +++ b/apidoc/View.yml @@ -286,8 +286,7 @@ methods: A cutout circle represents a special polygon that spans over the whole world and has a cutout in the shape of a circle. The radius of the shape, the overlay color and stroke properties can be configured. - exclude-platforms: [android] - since: { iphone: "12.3.0", ipad: "12.3.0", macos: "12.3.0" } + since: "12.4.0" parameters: - name: circleConfiguration summary: A object.