@lugg/maps 0.2.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/LICENSE +20 -0
  2. package/LuggMaps.podspec +23 -0
  3. package/README.md +119 -0
  4. package/android/build.gradle +78 -0
  5. package/android/gradle.properties +5 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/java/com/luggmaps/LuggMapsGoogleMapView.kt +438 -0
  8. package/android/src/main/java/com/luggmaps/LuggMapsGoogleMapViewManager.kt +144 -0
  9. package/android/src/main/java/com/luggmaps/LuggMapsMarkerView.kt +134 -0
  10. package/android/src/main/java/com/luggmaps/LuggMapsMarkerViewManager.kt +69 -0
  11. package/android/src/main/java/com/luggmaps/LuggMapsPackage.kt +13 -0
  12. package/android/src/main/java/com/luggmaps/LuggMapsPolylineView.kt +79 -0
  13. package/android/src/main/java/com/luggmaps/LuggMapsPolylineViewManager.kt +71 -0
  14. package/android/src/main/java/com/luggmaps/LuggMapsWrapperView.kt +39 -0
  15. package/android/src/main/java/com/luggmaps/LuggMapsWrapperViewManager.kt +25 -0
  16. package/android/src/main/java/com/luggmaps/core/PolylineAnimator.kt +180 -0
  17. package/android/src/main/java/com/luggmaps/events/CameraIdleEvent.kt +21 -0
  18. package/android/src/main/java/com/luggmaps/events/CameraMoveEvent.kt +28 -0
  19. package/app.plugin.js +1 -0
  20. package/ios/LuggMapsAppleMapView.h +16 -0
  21. package/ios/LuggMapsAppleMapView.mm +544 -0
  22. package/ios/LuggMapsGoogleMapView.h +13 -0
  23. package/ios/LuggMapsGoogleMapView.mm +439 -0
  24. package/ios/LuggMapsMarkerView.h +29 -0
  25. package/ios/LuggMapsMarkerView.mm +154 -0
  26. package/ios/LuggMapsPolylineView.h +27 -0
  27. package/ios/LuggMapsPolylineView.mm +116 -0
  28. package/ios/LuggMapsWrapperView.h +9 -0
  29. package/ios/LuggMapsWrapperView.mm +36 -0
  30. package/ios/core/GMSPolylineAnimator.h +11 -0
  31. package/ios/core/GMSPolylineAnimator.m +151 -0
  32. package/ios/core/MKPolylineAnimator.h +12 -0
  33. package/ios/core/MKPolylineAnimator.m +252 -0
  34. package/ios/core/PolylineAnimatorBase.h +22 -0
  35. package/ios/core/PolylineAnimatorBase.m +35 -0
  36. package/ios/events/CameraIdleEvent.h +24 -0
  37. package/ios/events/CameraMoveEvent.h +26 -0
  38. package/ios/extensions/MKMapView+Zoom.h +19 -0
  39. package/ios/extensions/MKMapView+Zoom.m +45 -0
  40. package/lib/module/MapView.js +87 -0
  41. package/lib/module/MapView.js.map +1 -0
  42. package/lib/module/MapView.types.js +4 -0
  43. package/lib/module/MapView.types.js.map +1 -0
  44. package/lib/module/Marker.js +34 -0
  45. package/lib/module/Marker.js.map +1 -0
  46. package/lib/module/Marker.types.js +4 -0
  47. package/lib/module/Marker.types.js.map +1 -0
  48. package/lib/module/Polyline.js +30 -0
  49. package/lib/module/Polyline.js.map +1 -0
  50. package/lib/module/Polyline.types.js +4 -0
  51. package/lib/module/Polyline.types.js.map +1 -0
  52. package/lib/module/fabric/LuggMapsAppleMapViewNativeComponent.ts +73 -0
  53. package/lib/module/fabric/LuggMapsGoogleMapViewNativeComponent.ts +74 -0
  54. package/lib/module/fabric/LuggMapsMarkerViewNativeComponent.ts +25 -0
  55. package/lib/module/fabric/LuggMapsPolylineViewNativeComponent.ts +19 -0
  56. package/lib/module/fabric/LuggMapsWrapperViewNativeComponent.ts +8 -0
  57. package/lib/module/index.js +6 -0
  58. package/lib/module/index.js.map +1 -0
  59. package/lib/module/package.json +1 -0
  60. package/lib/module/types.js +2 -0
  61. package/lib/module/types.js.map +1 -0
  62. package/lib/typescript/package.json +1 -0
  63. package/lib/typescript/plugin/src/index.d.ts +16 -0
  64. package/lib/typescript/plugin/src/index.d.ts.map +1 -0
  65. package/lib/typescript/plugin/src/withMapsAndroid.d.ts +6 -0
  66. package/lib/typescript/plugin/src/withMapsAndroid.d.ts.map +1 -0
  67. package/lib/typescript/plugin/src/withMapsIOS.d.ts +6 -0
  68. package/lib/typescript/plugin/src/withMapsIOS.d.ts.map +1 -0
  69. package/lib/typescript/src/MapView.d.ts +12 -0
  70. package/lib/typescript/src/MapView.d.ts.map +1 -0
  71. package/lib/typescript/src/MapView.types.d.ts +102 -0
  72. package/lib/typescript/src/MapView.types.d.ts.map +1 -0
  73. package/lib/typescript/src/Marker.d.ts +6 -0
  74. package/lib/typescript/src/Marker.d.ts.map +1 -0
  75. package/lib/typescript/src/Marker.types.d.ts +32 -0
  76. package/lib/typescript/src/Marker.types.d.ts.map +1 -0
  77. package/lib/typescript/src/Polyline.d.ts +6 -0
  78. package/lib/typescript/src/Polyline.d.ts.map +1 -0
  79. package/lib/typescript/src/Polyline.types.d.ts +24 -0
  80. package/lib/typescript/src/Polyline.types.d.ts.map +1 -0
  81. package/lib/typescript/src/fabric/LuggMapsAppleMapViewNativeComponent.d.ts +47 -0
  82. package/lib/typescript/src/fabric/LuggMapsAppleMapViewNativeComponent.d.ts.map +1 -0
  83. package/lib/typescript/src/fabric/LuggMapsGoogleMapViewNativeComponent.d.ts +48 -0
  84. package/lib/typescript/src/fabric/LuggMapsGoogleMapViewNativeComponent.d.ts.map +1 -0
  85. package/lib/typescript/src/fabric/LuggMapsMarkerViewNativeComponent.d.ts +20 -0
  86. package/lib/typescript/src/fabric/LuggMapsMarkerViewNativeComponent.d.ts.map +1 -0
  87. package/lib/typescript/src/fabric/LuggMapsPolylineViewNativeComponent.d.ts +15 -0
  88. package/lib/typescript/src/fabric/LuggMapsPolylineViewNativeComponent.d.ts.map +1 -0
  89. package/lib/typescript/src/fabric/LuggMapsWrapperViewNativeComponent.d.ts +6 -0
  90. package/lib/typescript/src/fabric/LuggMapsWrapperViewNativeComponent.d.ts.map +1 -0
  91. package/lib/typescript/src/index.d.ts +8 -0
  92. package/lib/typescript/src/index.d.ts.map +1 -0
  93. package/lib/typescript/src/types.d.ts +28 -0
  94. package/lib/typescript/src/types.d.ts.map +1 -0
  95. package/package.json +200 -0
  96. package/plugin/build/index.d.ts +15 -0
  97. package/plugin/build/index.js +13 -0
  98. package/plugin/build/withMapsAndroid.d.ts +5 -0
  99. package/plugin/build/withMapsAndroid.js +15 -0
  100. package/plugin/build/withMapsIOS.d.ts +5 -0
  101. package/plugin/build/withMapsIOS.js +27 -0
  102. package/src/MapView.tsx +111 -0
  103. package/src/MapView.types.ts +110 -0
  104. package/src/Marker.tsx +31 -0
  105. package/src/Marker.types.ts +32 -0
  106. package/src/Polyline.tsx +32 -0
  107. package/src/Polyline.types.ts +24 -0
  108. package/src/fabric/LuggMapsAppleMapViewNativeComponent.ts +73 -0
  109. package/src/fabric/LuggMapsGoogleMapViewNativeComponent.ts +74 -0
  110. package/src/fabric/LuggMapsMarkerViewNativeComponent.ts +25 -0
  111. package/src/fabric/LuggMapsPolylineViewNativeComponent.ts +19 -0
  112. package/src/fabric/LuggMapsWrapperViewNativeComponent.ts +8 -0
  113. package/src/index.ts +13 -0
  114. package/src/types.ts +30 -0
@@ -0,0 +1,116 @@
1
+ #import "LuggMapsPolylineView.h"
2
+
3
+ #import <react/renderer/components/RNMapsSpec/ComponentDescriptors.h>
4
+ #import <react/renderer/components/RNMapsSpec/EventEmitters.h>
5
+ #import <react/renderer/components/RNMapsSpec/Props.h>
6
+ #import <react/renderer/components/RNMapsSpec/RCTComponentViewHelpers.h>
7
+
8
+ #import "RCTFabricComponentsPlugins.h"
9
+ #import <React/RCTConversions.h>
10
+
11
+ using namespace facebook::react;
12
+
13
+ @interface LuggMapsPolylineView () <RCTLuggMapsPolylineViewViewProtocol>
14
+ @end
15
+
16
+ @implementation LuggMapsPolylineView {
17
+ NSArray<CLLocation *> *_coordinates;
18
+ NSArray<UIColor *> *_strokeColors;
19
+ BOOL _animated;
20
+ CGFloat _strokeWidth;
21
+ }
22
+
23
+ + (ComponentDescriptorProvider)componentDescriptorProvider {
24
+ return concreteComponentDescriptorProvider<
25
+ LuggMapsPolylineViewComponentDescriptor>();
26
+ }
27
+
28
+ - (instancetype)initWithFrame:(CGRect)frame {
29
+ if (self = [super initWithFrame:frame]) {
30
+ static const auto defaultProps =
31
+ std::make_shared<const LuggMapsPolylineViewProps>();
32
+ _props = defaultProps;
33
+
34
+ _coordinates = @[];
35
+ _strokeColors = @[ [UIColor blackColor] ];
36
+ _strokeWidth = 1.0;
37
+
38
+ self.hidden = YES;
39
+ }
40
+
41
+ return self;
42
+ }
43
+
44
+ - (void)updateProps:(Props::Shared const &)props
45
+ oldProps:(Props::Shared const &)oldProps {
46
+ [super updateProps:props oldProps:oldProps];
47
+ const auto &newViewProps =
48
+ *std::static_pointer_cast<LuggMapsPolylineViewProps const>(props);
49
+
50
+ NSMutableArray<CLLocation *> *coords = [NSMutableArray array];
51
+ for (const auto &coord : newViewProps.coordinates) {
52
+ CLLocation *location =
53
+ [[CLLocation alloc] initWithLatitude:coord.latitude
54
+ longitude:coord.longitude];
55
+ [coords addObject:location];
56
+ }
57
+ _coordinates = [coords copy];
58
+
59
+ NSMutableArray<UIColor *> *colors = [NSMutableArray array];
60
+ for (const auto &color : newViewProps.strokeColors) {
61
+ UIColor *uiColor = RCTUIColorFromSharedColor(color);
62
+ if (uiColor) {
63
+ [colors addObject:uiColor];
64
+ }
65
+ }
66
+ NSArray<UIColor *> *newColors =
67
+ colors.count > 0 ? [colors copy] : @[ [UIColor blackColor] ];
68
+ if (![newColors isEqualToArray:_strokeColors]) {
69
+ _strokeColors = newColors;
70
+ self.cachedSpans = nil;
71
+ }
72
+
73
+ _animated = newViewProps.animated;
74
+
75
+ _strokeWidth = newViewProps.strokeWidth > 0 ? newViewProps.strokeWidth : 1.0;
76
+ }
77
+
78
+ - (void)finalizeUpdates:(RNComponentViewUpdateMask)updateMask {
79
+ [super finalizeUpdates:updateMask];
80
+
81
+ if (updateMask & RNComponentViewUpdateMaskProps) {
82
+ if ([self.delegate respondsToSelector:@selector(polylineViewDidUpdate:)]) {
83
+ [self.delegate polylineViewDidUpdate:self];
84
+ }
85
+ }
86
+ }
87
+
88
+ - (NSArray<CLLocation *> *)coordinates {
89
+ return _coordinates;
90
+ }
91
+
92
+ - (NSArray<UIColor *> *)strokeColors {
93
+ return _strokeColors;
94
+ }
95
+
96
+ - (BOOL)animated {
97
+ return _animated;
98
+ }
99
+
100
+ - (CGFloat)strokeWidth {
101
+ return _strokeWidth;
102
+ }
103
+
104
+ - (void)prepareForRecycle {
105
+ [super prepareForRecycle];
106
+ self.polyline = nil;
107
+ self.renderer = nil;
108
+ self.cachedSpans = nil;
109
+ self.delegate = nil;
110
+ }
111
+
112
+ Class<RCTComponentViewProtocol> LuggMapsPolylineViewCls(void) {
113
+ return LuggMapsPolylineView.class;
114
+ }
115
+
116
+ @end
@@ -0,0 +1,9 @@
1
+ #import <React/RCTViewComponentView.h>
2
+ #import <UIKit/UIKit.h>
3
+
4
+ NS_ASSUME_NONNULL_BEGIN
5
+
6
+ @interface LuggMapsWrapperView : RCTViewComponentView
7
+ @end
8
+
9
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,36 @@
1
+ #import "LuggMapsWrapperView.h"
2
+
3
+ #import <react/renderer/components/RNMapsSpec/ComponentDescriptors.h>
4
+ #import <react/renderer/components/RNMapsSpec/EventEmitters.h>
5
+ #import <react/renderer/components/RNMapsSpec/Props.h>
6
+ #import <react/renderer/components/RNMapsSpec/RCTComponentViewHelpers.h>
7
+
8
+ #import "RCTFabricComponentsPlugins.h"
9
+
10
+ using namespace facebook::react;
11
+
12
+ @interface LuggMapsWrapperView () <RCTLuggMapsWrapperViewViewProtocol>
13
+ @end
14
+
15
+ @implementation LuggMapsWrapperView
16
+
17
+ + (ComponentDescriptorProvider)componentDescriptorProvider {
18
+ return concreteComponentDescriptorProvider<
19
+ LuggMapsWrapperViewComponentDescriptor>();
20
+ }
21
+
22
+ - (instancetype)initWithFrame:(CGRect)frame {
23
+ if (self = [super initWithFrame:frame]) {
24
+ static const auto defaultProps =
25
+ std::make_shared<const LuggMapsWrapperViewProps>();
26
+ _props = defaultProps;
27
+ }
28
+
29
+ return self;
30
+ }
31
+
32
+ Class<RCTComponentViewProtocol> LuggMapsWrapperViewCls(void) {
33
+ return LuggMapsWrapperView.class;
34
+ }
35
+
36
+ @end
@@ -0,0 +1,11 @@
1
+ #import "PolylineAnimatorBase.h"
2
+ #import <GoogleMaps/GoogleMaps.h>
3
+
4
+ @interface GMSPolylineAnimator : PolylineAnimatorBase <PolylineAnimator>
5
+
6
+ @property(nonatomic, weak) GMSPolyline *polyline;
7
+ @property(nonatomic, assign) BOOL animated;
8
+
9
+ - (void)update;
10
+
11
+ @end
@@ -0,0 +1,151 @@
1
+ #import "GMSPolylineAnimator.h"
2
+ #import <QuartzCore/QuartzCore.h>
3
+
4
+ @implementation GMSPolylineAnimator {
5
+ CADisplayLink *_displayLink;
6
+ CGFloat _animationProgress;
7
+ }
8
+
9
+ - (void)dealloc {
10
+ [self stopAnimation];
11
+ }
12
+
13
+ - (void)setAnimated:(BOOL)animated {
14
+ if (_animated == animated) {
15
+ return;
16
+ }
17
+ _animated = animated;
18
+
19
+ if (_animated) {
20
+ [self startAnimation];
21
+ } else {
22
+ [self stopAnimation];
23
+ [self update];
24
+ }
25
+ }
26
+
27
+ - (void)startAnimation {
28
+ if (_displayLink) {
29
+ return;
30
+ }
31
+ _animationProgress = 0;
32
+ _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(animationTick:)];
33
+ [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
34
+ }
35
+
36
+ - (void)stopAnimation {
37
+ [_displayLink invalidate];
38
+ _displayLink = nil;
39
+ }
40
+
41
+ - (void)animationTick:(CADisplayLink *)displayLink {
42
+ CGFloat speed = displayLink.duration / 1.75;
43
+ _animationProgress += speed;
44
+
45
+ if (_animationProgress >= 2.15) {
46
+ _animationProgress = 0;
47
+ }
48
+
49
+ [self updateAnimatedPolyline];
50
+ }
51
+
52
+ - (void)update {
53
+ if (_animated) {
54
+ return;
55
+ }
56
+
57
+ if (!_polyline || self.coordinates.count < 2) {
58
+ return;
59
+ }
60
+
61
+ GMSMutablePath *path = [GMSMutablePath path];
62
+ for (CLLocation *location in self.coordinates) {
63
+ [path addCoordinate:location.coordinate];
64
+ }
65
+ _polyline.path = path;
66
+
67
+ if (self.strokeColors.count > 1) {
68
+ _polyline.spans = [self createGradientSpans];
69
+ } else {
70
+ _polyline.strokeColor = self.strokeColors.firstObject ?: [UIColor blackColor];
71
+ }
72
+ }
73
+
74
+ - (void)updateAnimatedPolyline {
75
+ if (!_polyline || self.coordinates.count < 2) {
76
+ return;
77
+ }
78
+
79
+ NSUInteger segmentCount = self.coordinates.count - 1;
80
+ CGFloat progress = MIN(_animationProgress, 2.0);
81
+ CGFloat headPos, tailPos;
82
+
83
+ if (progress <= 1.0) {
84
+ tailPos = 0;
85
+ headPos = progress * segmentCount;
86
+ } else {
87
+ CGFloat shrinkProgress = progress - 1.0;
88
+ tailPos = shrinkProgress * segmentCount;
89
+ headPos = segmentCount;
90
+ }
91
+
92
+ if (headPos <= tailPos) {
93
+ _polyline.path = [GMSMutablePath path];
94
+ return;
95
+ }
96
+
97
+ NSUInteger startIndex = (NSUInteger)floor(tailPos);
98
+ NSUInteger endIndex = (NSUInteger)ceil(headPos);
99
+ CGFloat visibleLength = headPos - tailPos;
100
+
101
+ GMSMutablePath *path = [GMSMutablePath path];
102
+ NSMutableArray<GMSStyleSpan *> *spans = [NSMutableArray array];
103
+
104
+ for (NSUInteger i = startIndex; i <= endIndex && i < self.coordinates.count; i++) {
105
+ CLLocationCoordinate2D coord = self.coordinates[i].coordinate;
106
+
107
+ if (i == startIndex && tailPos > (CGFloat)startIndex) {
108
+ CGFloat t = tailPos - (CGFloat)startIndex;
109
+ CLLocationCoordinate2D nextCoord = self.coordinates[i + 1].coordinate;
110
+ coord.latitude = coord.latitude + (nextCoord.latitude - coord.latitude) * t;
111
+ coord.longitude = coord.longitude + (nextCoord.longitude - coord.longitude) * t;
112
+ }
113
+
114
+ if (i == endIndex && headPos < (CGFloat)endIndex && i > 0) {
115
+ CGFloat t = headPos - (CGFloat)(endIndex - 1);
116
+ CLLocationCoordinate2D prevCoord = self.coordinates[i - 1].coordinate;
117
+ coord.latitude = prevCoord.latitude + (coord.latitude - prevCoord.latitude) * t;
118
+ coord.longitude = prevCoord.longitude + (coord.longitude - prevCoord.longitude) * t;
119
+ }
120
+
121
+ [path addCoordinate:coord];
122
+
123
+ if (i < endIndex && i < segmentCount) {
124
+ CGFloat segStartPos = MAX((CGFloat)i, tailPos);
125
+ CGFloat segEndPos = MIN((CGFloat)(i + 1), headPos);
126
+ CGFloat gradientMid = ((segStartPos + segEndPos) / 2.0 - tailPos) / visibleLength;
127
+ UIColor *color = [self colorAtGradientPosition:gradientMid];
128
+ GMSStrokeStyle *style = [GMSStrokeStyle solidColor:color];
129
+ [spans addObject:[GMSStyleSpan spanWithStyle:style]];
130
+ }
131
+ }
132
+
133
+ _polyline.path = path;
134
+ _polyline.spans = spans;
135
+ }
136
+
137
+ - (NSArray<GMSStyleSpan *> *)createGradientSpans {
138
+ NSMutableArray<GMSStyleSpan *> *spans = [NSMutableArray array];
139
+ NSUInteger segmentCount = self.coordinates.count - 1;
140
+
141
+ for (NSUInteger i = 0; i < segmentCount; i++) {
142
+ CGFloat position = (CGFloat)i / (CGFloat)segmentCount;
143
+ UIColor *color = [self colorAtGradientPosition:position];
144
+ GMSStrokeStyle *style = [GMSStrokeStyle solidColor:color];
145
+ [spans addObject:[GMSStyleSpan spanWithStyle:style]];
146
+ }
147
+
148
+ return spans;
149
+ }
150
+
151
+ @end
@@ -0,0 +1,12 @@
1
+ #import <MapKit/MapKit.h>
2
+
3
+ @interface MKPolylineAnimator : MKOverlayPathRenderer
4
+
5
+ - (id)initWithPolyline:(MKPolyline *)polyline;
6
+
7
+ @property(nonatomic, strong) NSArray<UIColor *> *strokeColors;
8
+ @property(nonatomic, assign) BOOL animated;
9
+
10
+ - (void)updatePolyline:(MKPolyline *)polyline;
11
+
12
+ @end
@@ -0,0 +1,252 @@
1
+ #import "MKPolylineAnimator.h"
2
+ #import <QuartzCore/QuartzCore.h>
3
+
4
+ @implementation MKPolylineAnimator {
5
+ MKPolyline *_polyline;
6
+ CADisplayLink *_displayLink;
7
+ CGFloat _animationProgress; // 0→1 grow, 1→2 shrink
8
+ }
9
+
10
+ - (id)initWithPolyline:(MKPolyline *)polyline {
11
+ self = [super initWithOverlay:polyline];
12
+ if (self) {
13
+ _polyline = polyline;
14
+ _animationProgress = 0;
15
+ [self createPath];
16
+ }
17
+ return self;
18
+ }
19
+
20
+ - (void)dealloc {
21
+ [self stopAnimation];
22
+ }
23
+
24
+ - (void)setAnimated:(BOOL)animated {
25
+ if (_animated == animated) {
26
+ return;
27
+ }
28
+ _animated = animated;
29
+
30
+ if (_animated) {
31
+ [self startAnimation];
32
+ } else {
33
+ [self stopAnimation];
34
+ }
35
+ }
36
+
37
+ - (void)startAnimation {
38
+ if (_displayLink) {
39
+ return;
40
+ }
41
+ _animationProgress = 0;
42
+ _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(animationTick:)];
43
+ [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
44
+ }
45
+
46
+ - (void)stopAnimation {
47
+ [_displayLink invalidate];
48
+ _displayLink = nil;
49
+ }
50
+
51
+ - (void)animationTick:(CADisplayLink *)displayLink {
52
+ // ~1.75s per phase (grow or shrink), matching JS duration
53
+ CGFloat speed = displayLink.duration / 1.75;
54
+ _animationProgress += speed;
55
+
56
+ // 0→1 grow, 1→2 shrink, then reset with small pause
57
+ if (_animationProgress >= 2.15) { // 2.0 + 0.15 pause (~300ms at this speed)
58
+ _animationProgress = 0;
59
+ }
60
+
61
+ MKMapRect bounds = _polyline.boundingMapRect;
62
+ bounds = MKMapRectInset(bounds, -bounds.size.width * 0.1, -bounds.size.height * 0.1);
63
+ [self setNeedsDisplayInMapRect:bounds];
64
+ }
65
+
66
+ - (void)updatePolyline:(MKPolyline *)polyline {
67
+ _polyline = polyline;
68
+ [self invalidatePath];
69
+ [self createPath];
70
+ [self setNeedsDisplay];
71
+ }
72
+
73
+ - (void)createPath {
74
+ CGMutablePathRef path = CGPathCreateMutable();
75
+ BOOL first = YES;
76
+ for (NSUInteger i = 0; i < _polyline.pointCount; i++) {
77
+ CGPoint point = [self pointForMapPoint:_polyline.points[i]];
78
+ if (first) {
79
+ CGPathMoveToPoint(path, nil, point.x, point.y);
80
+ first = NO;
81
+ } else {
82
+ CGPathAddLineToPoint(path, nil, point.x, point.y);
83
+ }
84
+ }
85
+ self.path = path;
86
+ }
87
+
88
+ - (UIColor *)colorAtGradientPosition:(CGFloat)position {
89
+ if (!_strokeColors || _strokeColors.count == 0) {
90
+ return self.strokeColor;
91
+ }
92
+ if (_strokeColors.count == 1) {
93
+ return _strokeColors[0];
94
+ }
95
+
96
+ position = MAX(0, MIN(1, position));
97
+ CGFloat scaledPos = position * (_strokeColors.count - 1);
98
+ NSUInteger index = (NSUInteger)floor(scaledPos);
99
+ CGFloat t = scaledPos - index;
100
+
101
+ if (index >= _strokeColors.count - 1) {
102
+ return _strokeColors.lastObject;
103
+ }
104
+
105
+ UIColor *c1 = _strokeColors[index];
106
+ UIColor *c2 = _strokeColors[index + 1];
107
+
108
+ CGFloat r1, g1, b1, a1, r2, g2, b2, a2;
109
+ [c1 getRed:&r1 green:&g1 blue:&b1 alpha:&a1];
110
+ [c2 getRed:&r2 green:&g2 blue:&b2 alpha:&a2];
111
+
112
+ return [UIColor colorWithRed:r1 + (r2 - r1) * t
113
+ green:g1 + (g2 - g1) * t
114
+ blue:b1 + (b2 - b1) * t
115
+ alpha:a1 + (a2 - a1) * t];
116
+ }
117
+
118
+ - (void)drawMapRect:(MKMapRect)mapRect
119
+ zoomScale:(MKZoomScale)zoomScale
120
+ inContext:(CGContextRef)context {
121
+ CGRect pointsRect = CGPathGetBoundingBox(self.path);
122
+ CGRect mapRectCG = [self rectForMapRect:mapRect];
123
+ if (!CGRectIntersectsRect(pointsRect, mapRectCG)) {
124
+ return;
125
+ }
126
+
127
+ CGFloat lineWidth = self.lineWidth / zoomScale;
128
+ CGContextSetLineWidth(context, lineWidth);
129
+ CGContextSetLineCap(context, self.lineCap);
130
+ CGContextSetLineJoin(context, self.lineJoin);
131
+
132
+ NSUInteger segmentCount = _polyline.pointCount - 1;
133
+ if (segmentCount == 0) {
134
+ return;
135
+ }
136
+
137
+ // Snake animation: grow from start, then shrink from start
138
+ if (_animated && _polyline.pointCount > 1) {
139
+ CGFloat progress = MIN(_animationProgress, 2.0);
140
+ CGFloat headPos, tailPos;
141
+
142
+ if (progress <= 1.0) {
143
+ // Phase 1: grow from start to end
144
+ tailPos = 0;
145
+ headPos = progress * segmentCount;
146
+ } else {
147
+ // Phase 2: shrink from start
148
+ CGFloat shrinkProgress = progress - 1.0;
149
+ tailPos = shrinkProgress * segmentCount;
150
+ headPos = segmentCount;
151
+ }
152
+
153
+ if (headPos <= tailPos) {
154
+ return;
155
+ }
156
+
157
+ NSUInteger startIndex = (NSUInteger)floor(tailPos);
158
+ NSUInteger endIndex = (NSUInteger)ceil(headPos);
159
+ CGFloat visibleLength = headPos - tailPos;
160
+
161
+ for (NSUInteger i = startIndex; i < endIndex && i < segmentCount; i++) {
162
+ CGPoint segStart = [self pointForMapPoint:_polyline.points[i]];
163
+ CGPoint segEnd = [self pointForMapPoint:_polyline.points[i + 1]];
164
+
165
+ CGPoint drawStart = segStart;
166
+ CGPoint drawEnd = segEnd;
167
+ CGFloat segStartPos = (CGFloat)i;
168
+ CGFloat segEndPos = (CGFloat)(i + 1);
169
+
170
+ // Interpolate tail (partial segment at start)
171
+ if (segStartPos < tailPos) {
172
+ CGFloat t = tailPos - segStartPos;
173
+ drawStart.x = segStart.x + (segEnd.x - segStart.x) * t;
174
+ drawStart.y = segStart.y + (segEnd.y - segStart.y) * t;
175
+ segStartPos = tailPos;
176
+ }
177
+
178
+ // Interpolate head (partial segment at end)
179
+ if (segEndPos > headPos) {
180
+ CGFloat t = headPos - (CGFloat)i;
181
+ drawEnd.x = segStart.x + (segEnd.x - segStart.x) * t;
182
+ drawEnd.y = segStart.y + (segEnd.y - segStart.y) * t;
183
+ segEndPos = headPos;
184
+ }
185
+
186
+ // Calculate gradient position (0-1) within visible portion
187
+ CGFloat gradientStart = (segStartPos - tailPos) / visibleLength;
188
+ CGFloat gradientEnd = (segEndPos - tailPos) / visibleLength;
189
+
190
+ UIColor *startColor = [self colorAtGradientPosition:gradientStart];
191
+ UIColor *endColor = [self colorAtGradientPosition:gradientEnd];
192
+
193
+ // Draw gradient line segment
194
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
195
+ NSArray *colors = @[(__bridge id)startColor.CGColor, (__bridge id)endColor.CGColor];
196
+ CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, NULL);
197
+
198
+ CGContextSaveGState(context);
199
+ CGContextBeginPath(context);
200
+ CGContextMoveToPoint(context, drawStart.x, drawStart.y);
201
+ CGContextAddLineToPoint(context, drawEnd.x, drawEnd.y);
202
+ CGContextReplacePathWithStrokedPath(context);
203
+ CGContextClip(context);
204
+
205
+ CGContextDrawLinearGradient(context, gradient, drawStart, drawEnd, 0);
206
+
207
+ CGContextRestoreGState(context);
208
+ CGGradientRelease(gradient);
209
+ CGColorSpaceRelease(colorSpace);
210
+ }
211
+ return;
212
+ }
213
+
214
+ // Static gradient rendering
215
+ if (_strokeColors && _strokeColors.count > 1) {
216
+ for (NSUInteger i = 0; i < segmentCount; i++) {
217
+ CGPoint startPoint = [self pointForMapPoint:_polyline.points[i]];
218
+ CGPoint endPoint = [self pointForMapPoint:_polyline.points[i + 1]];
219
+
220
+ CGFloat gradientStart = (CGFloat)i / segmentCount;
221
+ CGFloat gradientEnd = (CGFloat)(i + 1) / segmentCount;
222
+
223
+ UIColor *startColor = [self colorAtGradientPosition:gradientStart];
224
+ UIColor *endColor = [self colorAtGradientPosition:gradientEnd];
225
+
226
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
227
+ NSArray *colors = @[(__bridge id)startColor.CGColor, (__bridge id)endColor.CGColor];
228
+ CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, NULL);
229
+
230
+ CGContextSaveGState(context);
231
+ CGContextBeginPath(context);
232
+ CGContextMoveToPoint(context, startPoint.x, startPoint.y);
233
+ CGContextAddLineToPoint(context, endPoint.x, endPoint.y);
234
+ CGContextReplacePathWithStrokedPath(context);
235
+ CGContextClip(context);
236
+
237
+ CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
238
+
239
+ CGContextRestoreGState(context);
240
+ CGGradientRelease(gradient);
241
+ CGColorSpaceRelease(colorSpace);
242
+ }
243
+ return;
244
+ }
245
+
246
+ // Single color
247
+ CGContextSetStrokeColorWithColor(context, self.strokeColor.CGColor);
248
+ CGContextAddPath(context, self.path);
249
+ CGContextStrokePath(context);
250
+ }
251
+
252
+ @end
@@ -0,0 +1,22 @@
1
+ #import <CoreLocation/CoreLocation.h>
2
+ #import <Foundation/Foundation.h>
3
+ #import <UIKit/UIKit.h>
4
+
5
+ @protocol PolylineAnimator <NSObject>
6
+
7
+ @property(nonatomic, strong) NSArray<CLLocation *> *coordinates;
8
+ @property(nonatomic, strong) NSArray<UIColor *> *strokeColors;
9
+ @property(nonatomic, assign) BOOL animated;
10
+
11
+ - (void)update;
12
+
13
+ @end
14
+
15
+ @interface PolylineAnimatorBase : NSObject
16
+
17
+ @property(nonatomic, strong) NSArray<CLLocation *> *coordinates;
18
+ @property(nonatomic, strong) NSArray<UIColor *> *strokeColors;
19
+
20
+ - (UIColor *)colorAtGradientPosition:(CGFloat)position;
21
+
22
+ @end
@@ -0,0 +1,35 @@
1
+ #import "PolylineAnimatorBase.h"
2
+
3
+ @implementation PolylineAnimatorBase
4
+
5
+ - (UIColor *)colorAtGradientPosition:(CGFloat)position {
6
+ if (!_strokeColors || _strokeColors.count == 0) {
7
+ return [UIColor blackColor];
8
+ }
9
+ if (_strokeColors.count == 1) {
10
+ return _strokeColors[0];
11
+ }
12
+
13
+ position = MAX(0, MIN(1, position));
14
+ CGFloat scaledPos = position * (_strokeColors.count - 1);
15
+ NSUInteger index = (NSUInteger)floor(scaledPos);
16
+ CGFloat t = scaledPos - index;
17
+
18
+ if (index >= _strokeColors.count - 1) {
19
+ return _strokeColors.lastObject;
20
+ }
21
+
22
+ UIColor *c1 = _strokeColors[index];
23
+ UIColor *c2 = _strokeColors[index + 1];
24
+
25
+ CGFloat r1, g1, b1, a1, r2, g2, b2, a2;
26
+ [c1 getRed:&r1 green:&g1 blue:&b1 alpha:&a1];
27
+ [c2 getRed:&r2 green:&g2 blue:&b2 alpha:&a2];
28
+
29
+ return [UIColor colorWithRed:r1 + (r2 - r1) * t
30
+ green:g1 + (g2 - g1) * t
31
+ blue:b1 + (b2 - b1) * t
32
+ alpha:a1 + (a2 - a1) * t];
33
+ }
34
+
35
+ @end
@@ -0,0 +1,24 @@
1
+ #pragma once
2
+
3
+ #import <react/renderer/components/RNMapsSpec/EventEmitters.h>
4
+
5
+ namespace luggmaps {
6
+ namespace events {
7
+
8
+ struct CameraIdleEvent {
9
+ double latitude;
10
+ double longitude;
11
+ double zoom;
12
+
13
+ template <typename Emitter>
14
+ void emit(std::shared_ptr<Emitter const> emitter) const {
15
+ typename Emitter::OnCameraIdle event;
16
+ event.coordinate.latitude = latitude;
17
+ event.coordinate.longitude = longitude;
18
+ event.zoom = zoom;
19
+ emitter->onCameraIdle(event);
20
+ }
21
+ };
22
+
23
+ } // namespace events
24
+ } // namespace luggmaps
@@ -0,0 +1,26 @@
1
+ #pragma once
2
+
3
+ #import <react/renderer/components/RNMapsSpec/EventEmitters.h>
4
+
5
+ namespace luggmaps {
6
+ namespace events {
7
+
8
+ struct CameraMoveEvent {
9
+ double latitude;
10
+ double longitude;
11
+ double zoom;
12
+ bool dragging;
13
+
14
+ template <typename Emitter>
15
+ void emit(std::shared_ptr<Emitter const> emitter) const {
16
+ typename Emitter::OnCameraMove event;
17
+ event.coordinate.latitude = latitude;
18
+ event.coordinate.longitude = longitude;
19
+ event.zoom = zoom;
20
+ event.dragging = dragging;
21
+ emitter->onCameraMove(event);
22
+ }
23
+ };
24
+
25
+ } // namespace events
26
+ } // namespace luggmaps