From 723db2caba06abbbb88844284453f32aafcf3125 Mon Sep 17 00:00:00 2001 From: zupao Date: Fri, 12 Apr 2019 08:51:47 +0800 Subject: [PATCH 01/27] [Android] Marker share icon image (#2741) * Add a test example to add massive number of markers on map * Share image for markers to reduce memory usage Use a shared image icon for markers instead of loading and creating bitmap for each marker, which uses more memory in case when we have a lot of markers but only use a limited set of images. --- example/App.js | 2 + example/examples/MassiveCustomMarkers.js | 131 ++++++++++++++++++ .../android/react/maps/AirMapManager.java | 11 +- .../android/react/maps/AirMapMarker.java | 51 ++++++- .../react/maps/AirMapMarkerManager.java | 128 ++++++++++++++++- .../airbnb/android/react/maps/AirMapView.java | 2 +- .../android/react/maps/MapsPackage.java | 1 + 7 files changed, 320 insertions(+), 6 deletions(-) create mode 100644 example/examples/MassiveCustomMarkers.js diff --git a/example/App.js b/example/App.js index 16ecb047d..3e8171723 100644 --- a/example/App.js +++ b/example/App.js @@ -45,6 +45,7 @@ import AnimatedNavigation from './examples/AnimatedNavigation'; import OnPoiClick from './examples/OnPoiClick'; import IndoorMap from './examples/IndoorMap'; import CameraControl from './examples/CameraControl'; +import MassiveCustomMarkers from './examples/MassiveCustomMarkers'; const IOS = Platform.OS === 'ios'; const ANDROID = Platform.OS === 'android'; @@ -169,6 +170,7 @@ export default class App extends React.Component { [OnPoiClick, 'On Poi Click', true], [IndoorMap, 'Indoor Map', true], [CameraControl, 'CameraControl', true], + [MassiveCustomMarkers, 'MassiveCustomMarkers', true], ] // Filter out examples that are not yet supported for Google Maps on iOS. .filter(example => ANDROID || (IOS && (example[2] || !this.state.useGoogleMaps))) diff --git a/example/examples/MassiveCustomMarkers.js b/example/examples/MassiveCustomMarkers.js new file mode 100644 index 000000000..6e9519886 --- /dev/null +++ b/example/examples/MassiveCustomMarkers.js @@ -0,0 +1,131 @@ +import React from 'react'; +import { + StyleSheet, + View, + Text, + Dimensions, + TouchableOpacity, +} from 'react-native'; + +import MapView, { Marker, ProviderPropType } from 'react-native-maps'; +import flagPinkImg from './assets/flag-pink.png'; + +const { width, height } = Dimensions.get('window'); + +const ASPECT_RATIO = width / height; +const LATITUDE = 37.78825; +const LONGITUDE = -122.4324; +const LATITUDE_DELTA = 0.0922; +const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; +let id = 0; + +class MassiveCustomMarkers extends React.Component { + constructor(props) { + super(props); + + this.state = { + region: { + latitude: LATITUDE, + longitude: LONGITUDE, + latitudeDelta: LATITUDE_DELTA, + longitudeDelta: LONGITUDE_DELTA, + }, + markers: [], + }; + + this.onMapPress = this.onMapPress.bind(this); + } + + generateMarkers(fromCoordinate) { + const result = []; + const { latitude, longitude } = fromCoordinate; + for (let i = 0; i < 100; i++) { + const newMarker = { + coordinate: { + latitude: latitude + (0.001 * i), + longitude: longitude + (0.001 * i), + }, + key: `foo${id++}`, + }; + result.push(newMarker); + } + return result; + } + + onMapPress(e) { + this.setState({ + markers: [ + ...this.state.markers, + ...this.generateMarkers(e.nativeEvent.coordinate), + ], + }); + } + + render() { + return ( + + + {this.state.markers.map(marker => ( + + ))} + + + this.setState({ markers: [] })} + style={styles.bubble} + > + Tap to create 100 markers + + + + ); + } +} + +MassiveCustomMarkers.propTypes = { + provider: ProviderPropType, +}; + +const styles = StyleSheet.create({ + container: { + ...StyleSheet.absoluteFillObject, + justifyContent: 'flex-end', + alignItems: 'center', + }, + map: { + ...StyleSheet.absoluteFillObject, + }, + bubble: { + backgroundColor: 'rgba(255,255,255,0.7)', + paddingHorizontal: 18, + paddingVertical: 12, + borderRadius: 20, + }, + latlng: { + width: 200, + alignItems: 'stretch', + }, + button: { + width: 80, + paddingHorizontal: 12, + alignItems: 'center', + marginHorizontal: 10, + }, + buttonContainer: { + flexDirection: 'row', + marginVertical: 20, + backgroundColor: 'transparent', + }, +}); + +export default MassiveCustomMarkers; diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapManager.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapManager.java index 637b52eaa..2be486af7 100644 --- a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapManager.java +++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapManager.java @@ -19,10 +19,9 @@ import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.LatLngBounds; import com.google.android.gms.maps.model.MapStyleOptions; -import com.google.maps.android.data.kml.KmlLayer; -import java.util.Map; import java.util.HashMap; +import java.util.Map; import javax.annotation.Nullable; @@ -52,6 +51,7 @@ public class AirMapManager extends ViewGroupManager { ); private final ReactApplicationContext appContext; + private AirMapMarkerManager markerManager; protected GoogleMapOptions googleMapOptions; @@ -60,6 +60,13 @@ public AirMapManager(ReactApplicationContext context) { this.googleMapOptions = new GoogleMapOptions(); } + public AirMapMarkerManager getMarkerManager() { + return this.markerManager; + } + public void setMarkerManager(AirMapMarkerManager markerManager) { + this.markerManager = markerManager; + } + @Override public String getName() { return REACT_CLASS; diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapMarker.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapMarker.java index faf63a8ee..85ffbb331 100644 --- a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapMarker.java +++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapMarker.java @@ -79,6 +79,8 @@ public class AirMapMarker extends AirMapFeature { private boolean hasViewChanges = true; private boolean hasCustomMarkerView = false; + private final AirMapMarkerManager markerManager; + private String imageUri; private final DraweeHolder logoHolder; private DataSource> dataSource; @@ -110,20 +112,26 @@ public void onFinalImageSet( CloseableReference.closeSafely(imageReference); } } + if (AirMapMarker.this.markerManager != null && AirMapMarker.this.imageUri != null) { + AirMapMarker.this.markerManager.getSharedIcon(AirMapMarker.this.imageUri) + .updateIcon(iconBitmapDescriptor, iconBitmap); + } update(true); } }; - public AirMapMarker(Context context) { + public AirMapMarker(Context context, AirMapMarkerManager markerManager) { super(context); this.context = context; + this.markerManager = markerManager; logoHolder = DraweeHolder.create(createDraweeHierarchy(), context); logoHolder.onAttach(); } - public AirMapMarker(Context context, MarkerOptions options) { + public AirMapMarker(Context context, MarkerOptions options, AirMapMarkerManager markerManager) { super(context); this.context = context; + this.markerManager = markerManager; logoHolder = DraweeHolder.create(createDraweeHierarchy(), context); logoHolder.onAttach(); @@ -316,6 +324,31 @@ public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) { public void setImage(String uri) { hasViewChanges = true; + boolean shouldLoadImage = true; + + if (this.markerManager != null) { + // remove marker from previous shared icon if needed, to avoid future updates from it. + // remove the shared icon completely if no markers on it as well. + // this is to avoid memory leak due to orphan bitmaps. + // + // However in case where client want to update all markers from icon A to icon B + // and after some time to update back from icon B to icon A + // it may be better to keep it though. We assume that is rare. + if (this.imageUri != null) { + this.markerManager.getSharedIcon(this.imageUri).removeMarker(this); + this.markerManager.removeSharedIconIfEmpty(this.imageUri); + } + if (uri != null) { + // listening for marker bitmap descriptor update, as well as check whether to load the image. + AirMapMarkerManager.AirMapMarkerSharedIcon sharedIcon = this.markerManager.getSharedIcon(uri); + sharedIcon.addMarker(this); + shouldLoadImage = sharedIcon.shouldLoadImage(); + } + } + + this.imageUri = uri; + if (!shouldLoadImage) {return;} + if (uri == null) { iconBitmapDescriptor = null; update(true); @@ -346,10 +379,24 @@ public void setImage(String uri) { drawable.draw(canvas); } } + if (this.markerManager != null && uri != null) { + this.markerManager.getSharedIcon(uri).updateIcon(iconBitmapDescriptor, iconBitmap); + } update(true); } } + public void setIconBitmapDescriptor(BitmapDescriptor bitmapDescriptor, Bitmap bitmap) { + this.iconBitmapDescriptor = bitmapDescriptor; + this.iconBitmap = bitmap; + this.hasViewChanges = true; + this.update(true); + } + + public void setIconBitmap(Bitmap bitmap) { + this.iconBitmap = bitmap; + } + public MarkerOptions getMarkerOptions() { if (markerOptions == null) { markerOptions = new MarkerOptions(); diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapMarkerManager.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapMarkerManager.java index 7676a453a..e89548ca4 100644 --- a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapMarkerManager.java +++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapMarkerManager.java @@ -1,5 +1,6 @@ package com.airbnb.android.react.maps; +import android.graphics.Bitmap; import android.graphics.Color; import android.view.View; @@ -11,10 +12,13 @@ import com.facebook.react.uimanager.ViewGroupManager; import com.facebook.react.uimanager.annotations.ReactProp; import com.google.android.gms.maps.model.Marker; +import com.google.android.gms.maps.model.BitmapDescriptor; import com.google.android.gms.maps.model.LatLng; import java.util.HashMap; import java.util.Map; +import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; import javax.annotation.Nullable; @@ -25,6 +29,128 @@ public class AirMapMarkerManager extends ViewGroupManager { private static final int ANIMATE_MARKER_TO_COORDINATE = 3; private static final int REDRAW = 4; + public static class AirMapMarkerSharedIcon { + private BitmapDescriptor iconBitmapDescriptor; + private Bitmap bitmap; + private Map markers; + private boolean loadImageStarted; + + public AirMapMarkerSharedIcon(){ + this.markers = new WeakHashMap<>(); + this.loadImageStarted = false; + } + + /** + * check whether the load image process started. + * caller AirMapMarker will only need to load it when this returns true. + * + * @return true if it is not started, false otherwise. + */ + public synchronized boolean shouldLoadImage(){ + if (!this.loadImageStarted) { + this.loadImageStarted = true; + return true; + } + return false; + } + + /** + * subscribe icon update for given marker. + * + * The marker is wrapped in weakReference, so no need to remove it explicitly. + * + * @param marker + */ + public synchronized void addMarker(AirMapMarker marker) { + this.markers.put(marker, true); + if (this.iconBitmapDescriptor != null) { + marker.setIconBitmapDescriptor(this.iconBitmapDescriptor, this.bitmap); + } + } + + /** + * Remove marker from this shared icon. + * + * Marker will only need to call it when the marker receives a different marker image uri. + * + * @param marker + */ + public synchronized void removeMarker(AirMapMarker marker) { + this.markers.remove(marker); + } + + /** + * check if there is markers still listening on this icon. + * when there are not markers listen on it, we can remove it. + * + * @return true if there is, false otherwise + */ + public synchronized boolean hasMarker(){ + return this.markers.isEmpty(); + } + + /** + * Update the bitmap descriptor and bitmap for the image uri. + * And notify all subscribers about the update. + * + * @param bitmapDescriptor + * @param bitmap + */ + public synchronized void updateIcon(BitmapDescriptor bitmapDescriptor, Bitmap bitmap) { + + this.iconBitmapDescriptor = bitmapDescriptor; + this.bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true); + + if (this.markers.isEmpty()) { + return; + } + + for (Map.Entry markerEntry: markers.entrySet()) { + if (markerEntry.getKey() != null) { + markerEntry.getKey().setIconBitmapDescriptor(bitmapDescriptor, bitmap); + } + } + } + } + + private Map sharedIcons = new ConcurrentHashMap<>(); + + /** + * get the shared icon object, if not existed, create a new one and store it. + * + * @param uri + * @return the icon object for the given uri. + */ + public AirMapMarkerSharedIcon getSharedIcon(String uri) { + AirMapMarkerSharedIcon icon = this.sharedIcons.get(uri); + if (icon == null) { + synchronized (this) { + if((icon = this.sharedIcons.get(uri)) == null) { + icon = new AirMapMarkerSharedIcon(); + this.sharedIcons.put(uri, icon); + } + } + } + return icon; + } + + /** + * Remove the share icon object from our sharedIcons map when no markers are listening for it. + * + * @param uri + */ + public void removeSharedIconIfEmpty(String uri) { + AirMapMarkerSharedIcon icon = this.sharedIcons.get(uri); + if (icon == null) {return;} + if (!icon.hasMarker()) { + synchronized (this) { + if((icon = this.sharedIcons.get(uri)) != null && !icon.hasMarker()) { + this.sharedIcons.remove(uri); + } + } + } + } + public AirMapMarkerManager() { } @@ -35,7 +161,7 @@ public String getName() { @Override public AirMapMarker createViewInstance(ThemedReactContext context) { - return new AirMapMarker(context); + return new AirMapMarker(context, this); } @ReactProp(name = "coordinate") diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapView.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapView.java index d70fd4ac0..20752b644 100644 --- a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapView.java +++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapView.java @@ -1110,7 +1110,7 @@ public void setKmlSrc(String kmlSrc) { options.title(title); options.snippet(snippet); - AirMapMarker marker = new AirMapMarker(context, options); + AirMapMarker marker = new AirMapMarker(context, options, this.manager.getMarkerManager()); if (placemark.getInlineStyle() != null && placemark.getInlineStyle().getIconUrl() != null) { diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/MapsPackage.java b/lib/android/src/main/java/com/airbnb/android/react/maps/MapsPackage.java index 45364b3be..9a9bd4e62 100644 --- a/lib/android/src/main/java/com/airbnb/android/react/maps/MapsPackage.java +++ b/lib/android/src/main/java/com/airbnb/android/react/maps/MapsPackage.java @@ -41,6 +41,7 @@ public List createViewManagers(ReactApplicationContext reactContext AirMapUrlTileManager urlTileManager = new AirMapUrlTileManager(reactContext); AirMapLocalTileManager localTileManager = new AirMapLocalTileManager(reactContext); AirMapOverlayManager overlayManager = new AirMapOverlayManager(reactContext); + mapManager.setMarkerManager(annotationManager); return Arrays.asList( calloutManager, From 68039f91ae7ff7df1e5e4260734ae831b701fb87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Chorne?= <1090696+stpch@users.noreply.github.com> Date: Sat, 13 Apr 2019 17:55:04 +0200 Subject: [PATCH 02/27] Fix PR links in CHANGELOG.md (#2808) * Fix PR links * Fix PR links --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f575e3e41..cc82712d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,18 +2,18 @@ ## 0.24.0 (April 11, 2019 ) * Common: [#2740](https://github.com/react-native-community/react-native-maps/pull/2740) Fix deprecated UIManager usage when accessing component names * Common: [#2393](https://github.com/react-native-community/react-native-maps/pull/2393) add typings for pointForCoordinate & coordinateForPoint -* Common: [#2732](https://github.com/react-native-community/react-native-maps/pull/2393) Implement ability to flip y coordinate for Google Map tiles. -* Android: [#2765](https://github.com/react-native-community/react-native-maps/pull/2764) Allow setting of play-services version through ext +* Common: [#2732](https://github.com/react-native-community/react-native-maps/pull/2732) Implement ability to flip y coordinate for Google Map tiles. +* Android: [#2765](https://github.com/react-native-community/react-native-maps/pull/2765) Allow setting of play-services version through ext * Android: [#2702](https://github.com/react-native-community/react-native-maps/pull/2702) Enable RN projects to define the Android AppCompat Library version * Android: [#2720](https://github.com/react-native-community/react-native-maps/pull/2720) Fix Android dependencies and build errors * Android: [#2682](https://github.com/react-native-community/react-native-maps/pull/2682) Implement 'tappable' prop on polyline for Android * Android: [#2417](https://github.com/react-native-community/react-native-maps/pull/2417) Support for lineCap and lineDash pattern -* Android: [#2727](https://github.com/react-native-community/react-native-maps/pull/2417) fix build: only apply mvn push gradle plugin if POM_ARTIFACT_ID is set +* Android: [#2727](https://github.com/react-native-community/react-native-maps/pull/2727) fix build: only apply mvn push gradle plugin if POM_ARTIFACT_ID is set * iOS: [#2446](https://github.com/react-native-community/react-native-maps/pull/2446) fix iOS GoogleMaps camera always animate * iOS: [#2746](https://github.com/react-native-community/react-native-maps/pull/2746) onPanDrag support for iOS * iOS: [#2581](https://github.com/react-native-community/react-native-maps/pull/2581) Custom callout improvements 🎉 * iOS: [#2794](https://github.com/react-native-community/react-native-maps/pull/2794) Fix CalloutSubview on Apple maps -* iOS: [#2716](https://github.com/react-native-community/react-native-maps/pull/2716) Fix Memory Leaks +* iOS: [#2716](https://github.com/react-native-community/react-native-maps/pull/2716) Fix Memory Leaks * Docs: [#2675](https://github.com/react-native-community/react-native-maps/pull/2675) [#2685](https://github.com/react-native-community/react-native-maps/pull/2685) [#2707](https://github.com/react-native-community/react-native-maps/pull/2707) [#2704](https://github.com/react-native-community/react-native-maps/pull/2704) * Example: [#2792](https://github.com/react-native-community/react-native-maps/pull/2792) Upgrade Example to react-native to 0.59.3 * TypeScript: [#2705](https://github.com/react-native-community/react-native-maps/pull/2705) Add Marker icon property introduced in [#2650](https://github.com/react-native-community/react-native-maps/pull/2650) to index.d.ts From fd745b7f5460b400d9a5cbd1561a033c803efe2d Mon Sep 17 00:00:00 2001 From: Casey Tickes Date: Sat, 13 Apr 2019 09:04:41 -0700 Subject: [PATCH 03/27] Handle annotations without images on iOS map snapshot (#2423) * Render annotation view heirarchy to CGContext if annotation does not have an image. * add annotations to snapshot image after overlays. --- lib/ios/AirMaps/AIRMapManager.m | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/lib/ios/AirMaps/AIRMapManager.m b/lib/ios/AirMaps/AIRMapManager.m index 08393f176..7588ba163 100644 --- a/lib/ios/AirMaps/AIRMapManager.m +++ b/lib/ios/AirMaps/AIRMapManager.m @@ -566,25 +566,30 @@ - (void)takeMapSnapshot:(AIRMap *)mapView CGRect rect = CGRectMake(0.0f, 0.0f, image.size.width, image.size.height); + for (id overlay in mapView.overlays) { + if ([overlay respondsToSelector:@selector(drawToSnapshot:context:)]) { + [overlay drawToSnapshot:snapshot context:UIGraphicsGetCurrentContext()]; + } + } + for (id annotation in mapView.annotations) { CGPoint point = [snapshot pointForCoordinate:annotation.coordinate]; - + MKAnnotationView* anView = [mapView viewForAnnotation: annotation]; - + if (anView){ pin = anView; } - + if (CGRectContainsPoint(rect, point)) { point.x = point.x + pin.centerOffset.x - (pin.bounds.size.width / 2.0f); point.y = point.y + pin.centerOffset.y - (pin.bounds.size.height / 2.0f); - [pin.image drawAtPoint:point]; - } - } - - for (id overlay in mapView.overlays) { - if ([overlay respondsToSelector:@selector(drawToSnapshot:context:)]) { - [overlay drawToSnapshot:snapshot context:UIGraphicsGetCurrentContext()]; + if (pin.image) { + [pin.image drawAtPoint:point]; + } else { + CGRect pinRect = CGRectMake(point.x, point.y, pin.bounds.size.width, pin.bounds.size.height); + [pin drawViewHierarchyInRect:pinRect afterScreenUpdates:NO]; + } } } From 233d30565181d91242528ed451da3026b5f2c35e Mon Sep 17 00:00:00 2001 From: Dmitriy Nikifrov Date: Sat, 13 Apr 2019 21:05:02 +0500 Subject: [PATCH 04/27] Update AirMapMarker.java (#2557) --- .../main/java/com/airbnb/android/react/maps/AirMapMarker.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapMarker.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapMarker.java index 85ffbb331..6cc30a728 100644 --- a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapMarker.java +++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapMarker.java @@ -287,7 +287,7 @@ public boolean updateCustomForTracking() { } public void updateMarkerIcon() { - if (!hasViewChanges) return; + if (marker == null) return; if (!hasCustomMarkerView) { // No more updates for this, as it's a simple icon @@ -428,6 +428,7 @@ public void requestLayout() { updateTracksViewChanges(); update(true); } + } } From 3189d8fadb3b3caf92b9832b732e08767960defa Mon Sep 17 00:00:00 2001 From: Christopher Dro Date: Sat, 13 Apr 2019 09:09:21 -0700 Subject: [PATCH 05/27] Remove folders in .gitignore --- .bundle/config | 2 - .idea/codeStyleSettings.xml | 265 ------------------------------------ 2 files changed, 267 deletions(-) delete mode 100644 .bundle/config delete mode 100644 .idea/codeStyleSettings.xml diff --git a/.bundle/config b/.bundle/config deleted file mode 100644 index f7382bd2c..000000000 --- a/.bundle/config +++ /dev/null @@ -1,2 +0,0 @@ ---- -BUNDLE_PATH: "./example/ios/bundles" diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml deleted file mode 100644 index b6cfecda9..000000000 --- a/.idea/codeStyleSettings.xml +++ /dev/null @@ -1,265 +0,0 @@ - - - - - - \ No newline at end of file From 3140e1c19f9ace29f6ad0f9aa5f944d7c791cb27 Mon Sep 17 00:00:00 2001 From: Eric James Date: Sat, 13 Apr 2019 13:57:45 -0400 Subject: [PATCH 06/27] Note that children components can be added (#2326) I literally just trial and errorred this after banging my head. --- docs/marker.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/marker.md b/docs/marker.md index a91183292..b57a4b335 100644 --- a/docs/marker.md +++ b/docs/marker.md @@ -65,3 +65,16 @@ type Point { y: Number, } ``` + +## Children Components + +Children components can be added within a Marker and rendered content will replace the marker symbol. This is a way of creating custom markers and allowing use of native SVGs. + +Example: +``` + + + SF + + +``` From e749b9b50bd6ee60f4fc41bd4536f261d342a23b Mon Sep 17 00:00:00 2001 From: Christopher Dro Date: Sun, 14 Apr 2019 15:48:00 -0700 Subject: [PATCH 07/27] [GoogleMaps] Update pod SDK to 3.0.3 (#2812) [GoogleMaps] Update podspec to use GoogleMaps 3.0.3 --- .../AirMapsExplorer.xcodeproj/project.pbxproj | 2 ++ example/ios/Podfile.lock | 16 ++++++++-------- react-native-google-maps.podspec | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/example/ios/AirMapsExplorer.xcodeproj/project.pbxproj b/example/ios/AirMapsExplorer.xcodeproj/project.pbxproj index 08483533a..7e24b830b 100644 --- a/example/ios/AirMapsExplorer.xcodeproj/project.pbxproj +++ b/example/ios/AirMapsExplorer.xcodeproj/project.pbxproj @@ -451,6 +451,7 @@ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, ); INFOPLIST_FILE = AirMapsExplorer/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = AirMapsExplorer; @@ -473,6 +474,7 @@ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, ); INFOPLIST_FILE = AirMapsExplorer/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = AirMapsExplorer; diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 2bcb3b987..f57b11d3f 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -22,16 +22,16 @@ PODS: - GoogleMaps - Google-Maps-iOS-Utils/QuadTree (2.1.0): - GoogleMaps - - GoogleMaps (2.5.0): - - GoogleMaps/Maps (= 2.5.0) - - GoogleMaps/Base (2.5.0) - - GoogleMaps/Maps (2.5.0): + - GoogleMaps (3.0.3): + - GoogleMaps/Maps (= 3.0.3) + - GoogleMaps/Base (3.0.3) + - GoogleMaps/Maps (3.0.3): - GoogleMaps/Base - React (0.59.3): - React/Core (= 0.59.3) - react-native-google-maps (0.24.0): - Google-Maps-iOS-Utils (= 2.1.0) - - GoogleMaps (= 2.5.0) + - GoogleMaps (= 3.0.3) - React - react-native-maps (0.24.0): - React @@ -141,10 +141,10 @@ SPEC CHECKSUMS: Folly: de497beb10f102453a1afa9edbf8cf8a251890de glog: aefd1eb5dda2ab95ba0938556f34b98e2da3a60d Google-Maps-iOS-Utils: c32891ff472eaaa1fca032beedafa1a013af7875 - GoogleMaps: c087b8e5dfe87ca6ebf59adb9b4894a4146bec4f + GoogleMaps: fd5c9fa58871781b9fb94d4936f19c69fe60ce32 React: b52fdb80565505b7e4592b313a38868f5df51641 - react-native-google-maps: dd3cee2ea5d77ffba4f03b1fd64891e5113b3193 - react-native-maps: 7525a32bcd025af8cf3b6c469af9b8758245db72 + react-native-google-maps: 6142058c902e207524e1d92277d749cf9e2c3bd7 + react-native-maps: d87d8ed456d4132112fb1d2ee9baf3470c0f211d yoga: 1a492113f66a35e9e583bb0434f4b8cc668dd839 PODFILE CHECKSUM: d2a6b35e414b69177ba0b16fb76ad1eb91a4a929 diff --git a/react-native-google-maps.podspec b/react-native-google-maps.podspec index ea7318320..9f9683de0 100644 --- a/react-native-google-maps.podspec +++ b/react-native-google-maps.podspec @@ -17,6 +17,6 @@ Pod::Spec.new do |s| s.compiler_flags = '-DHAVE_GOOGLE_MAPS=1', '-DHAVE_GOOGLE_MAPS_UTILS=1', '-fno-modules' s.dependency 'React' - s.dependency 'GoogleMaps', '2.5.0' + s.dependency 'GoogleMaps', '3.0.3' s.dependency 'Google-Maps-iOS-Utils', '2.1.0' end From ce80b43f78c475fae84283409ea33122e0fa1fdc Mon Sep 17 00:00:00 2001 From: Nizam Moidu Date: Mon, 15 Apr 2019 03:30:15 +0400 Subject: [PATCH 08/27] Support for WMS Layer support (#2568) * [WIP] Geoserver layer Apple Maps * [WIP] Google Maps layer doesnt work yet * [iOS] [GoogleMap] support for WMS layers * updated AirMaps xcode project with new sources * [Android] support for WMS layer * refactor to MapWMSTile * react example for WMS tile * WMS Tile sample list item * lint corrections --- example/App.js | 2 + example/examples/WMSTiles.js | 105 ++++++++++++ index.d.ts | 15 ++ index.js | 1 + .../airbnb/android/react/maps/AirMapView.java | 4 + .../android/react/maps/AirMapWMSTile.java | 156 ++++++++++++++++++ .../react/maps/AirMapWMSTileManager.java | 67 ++++++++ .../android/react/maps/MapsPackage.java | 2 + lib/components/MapView.js | 2 + lib/components/MapWMSTile.js | 87 ++++++++++ lib/ios/AirGoogleMaps/AIRGoogleMap.m | 9 + lib/ios/AirGoogleMaps/AIRGoogleMapWMSTile.h | 33 ++++ lib/ios/AirGoogleMaps/AIRGoogleMapWMSTile.m | 125 ++++++++++++++ .../AIRGoogleMapWMSTileManager.h | 17 ++ .../AIRGoogleMapWMSTileManager.m | 37 +++++ lib/ios/AirMaps.xcodeproj/project.pbxproj | 29 +++- lib/ios/AirMaps/AIRMap.m | 4 + lib/ios/AirMaps/AIRMapManager.m | 3 + lib/ios/AirMaps/AIRMapWMSTile.h | 41 +++++ lib/ios/AirMaps/AIRMapWMSTile.m | 149 +++++++++++++++++ lib/ios/AirMaps/AIRMapWMSTileManager.h | 14 ++ lib/ios/AirMaps/AIRMapWMSTileManager.m | 42 +++++ 22 files changed, 942 insertions(+), 2 deletions(-) create mode 100644 example/examples/WMSTiles.js create mode 100644 lib/android/src/main/java/com/airbnb/android/react/maps/AirMapWMSTile.java create mode 100644 lib/android/src/main/java/com/airbnb/android/react/maps/AirMapWMSTileManager.java create mode 100644 lib/components/MapWMSTile.js create mode 100644 lib/ios/AirGoogleMaps/AIRGoogleMapWMSTile.h create mode 100644 lib/ios/AirGoogleMaps/AIRGoogleMapWMSTile.m create mode 100644 lib/ios/AirGoogleMaps/AIRGoogleMapWMSTileManager.h create mode 100644 lib/ios/AirGoogleMaps/AIRGoogleMapWMSTileManager.m create mode 100644 lib/ios/AirMaps/AIRMapWMSTile.h create mode 100644 lib/ios/AirMaps/AIRMapWMSTile.m create mode 100644 lib/ios/AirMaps/AIRMapWMSTileManager.h create mode 100644 lib/ios/AirMaps/AIRMapWMSTileManager.m diff --git a/example/App.js b/example/App.js index 3e8171723..5b24483d9 100644 --- a/example/App.js +++ b/example/App.js @@ -31,6 +31,7 @@ import FitToSuppliedMarkers from './examples/FitToSuppliedMarkers'; import FitToCoordinates from './examples/FitToCoordinates'; import LiteMapView from './examples/LiteMapView'; import CustomTiles from './examples/CustomTiles'; +import WMSTiles from './examples/WMSTiles'; import ZIndexMarkers from './examples/ZIndexMarkers'; import StaticMap from './examples/StaticMap'; import MapStyle from './examples/MapStyle'; @@ -157,6 +158,7 @@ export default class App extends React.Component { [FitToCoordinates, 'Fit Map To Coordinates', true], [LiteMapView, 'Android Lite MapView'], [CustomTiles, 'Custom Tiles', true], + [WMSTiles, 'WMS Tiles', true], [ZIndexMarkers, 'Position Markers with Z-index', true], [MapStyle, 'Customize the style of the map', true], [LegalLabel, 'Reposition the legal label', true], diff --git a/example/examples/WMSTiles.js b/example/examples/WMSTiles.js new file mode 100644 index 000000000..a03a9e519 --- /dev/null +++ b/example/examples/WMSTiles.js @@ -0,0 +1,105 @@ +import React from 'react'; +import { + StyleSheet, + View, + Text, + Dimensions, +} from 'react-native'; + +import MapView, { MAP_TYPES, ProviderPropType, WMSTile } from 'react-native-maps'; + +const { width, height } = Dimensions.get('window'); + +const ASPECT_RATIO = width / height; +const LATITUDE = 40.71095; +const LONGITUDE = -74.00997; +const LATITUDE_DELTA = 0.0152; +const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; + +class WMSTiles extends React.Component { + constructor(props, context) { + super(props, context); + + this.state = { + region: { + latitude: LATITUDE, + longitude: LONGITUDE, + latitudeDelta: LATITUDE_DELTA, + longitudeDelta: LONGITUDE_DELTA, + }, + }; + } + + render() { + const { region } = this.state; + return ( + + + + + + + WMS Tiles + + + + ); + } +} + +WMSTiles.propTypes = { + provider: ProviderPropType, +}; + +const styles = StyleSheet.create({ + container: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + justifyContent: 'flex-end', + alignItems: 'center', + }, + map: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + }, + bubble: { + flex: 1, + backgroundColor: 'rgba(255,255,255,0.7)', + paddingHorizontal: 18, + paddingVertical: 12, + borderRadius: 20, + }, + latlng: { + width: 200, + alignItems: 'stretch', + }, + button: { + width: 80, + paddingHorizontal: 12, + alignItems: 'center', + marginHorizontal: 10, + }, + buttonContainer: { + flexDirection: 'row', + marginVertical: 20, + backgroundColor: 'transparent', + }, +}); + +export default WMSTiles; diff --git a/index.d.ts b/index.d.ts index 2db2ba119..2637a05d2 100644 --- a/index.d.ts +++ b/index.d.ts @@ -422,6 +422,21 @@ declare module "react-native-maps" { export class LocalTile extends React.Component { } + // ======================================================================= + // WMSTile + // ======================================================================= + + export interface MapWMSTileProps extends ViewProperties { + urlTemplate: string; + maximumZ?: number; + minimumZ?: number; + tileSize: number; + opacity: number; + zIndex?: number; + } + + export class WMSTile extends React.Component { + } // ======================================================================= // Overlay // ======================================================================= diff --git a/index.js b/index.js index 37582ca7e..af51ad95e 100644 --- a/index.js +++ b/index.js @@ -6,6 +6,7 @@ export { default as Polyline } from './lib/components/MapPolyline.js'; export { default as Polygon } from './lib/components/MapPolygon.js'; export { default as Circle } from './lib/components/MapCircle.js'; export { default as UrlTile } from './lib/components/MapUrlTile.js'; +export { default as WMSTile } from './lib/components/MapWMSTile.js'; export { default as LocalTile } from './lib/components/MapLocalTile.js'; export { default as Callout } from './lib/components/MapCallout.js'; export { default as CalloutSubview } from './lib/components/MapCalloutSubview.js'; diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapView.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapView.java index 20752b644..397d4a6f4 100644 --- a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapView.java +++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapView.java @@ -617,6 +617,10 @@ public void addFeature(View child, int index) { AirMapUrlTile urlTileView = (AirMapUrlTile) child; urlTileView.addToMap(map); features.add(index, urlTileView); + } else if (child instanceof AirMapWMSTile) { + AirMapWMSTile urlTileView = (AirMapWMSTile) child; + urlTileView.addToMap(map); + features.add(index, urlTileView); } else if (child instanceof AirMapLocalTile) { AirMapLocalTile localTileView = (AirMapLocalTile) child; localTileView.addToMap(map); diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapWMSTile.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapWMSTile.java new file mode 100644 index 000000000..e789d2bd0 --- /dev/null +++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapWMSTile.java @@ -0,0 +1,156 @@ +package com.airbnb.android.react.maps; + +import android.content.Context; + +import com.google.android.gms.maps.GoogleMap; +import com.google.android.gms.maps.model.TileOverlay; +import com.google.android.gms.maps.model.TileOverlayOptions; +import com.google.android.gms.maps.model.UrlTileProvider; + +import java.net.MalformedURLException; +import java.net.URL; + +public class AirMapWMSTile extends AirMapFeature { + private static final double[] mapBound = {-20037508.34789244, 20037508.34789244}; + private static final double FULL = 20037508.34789244 * 2; + + class AIRMapGSUrlTileProvider extends UrlTileProvider { + private String urlTemplate; + private int width; + private int height; + public AIRMapGSUrlTileProvider(int width, int height, String urlTemplate) { + super(width, height); + this.urlTemplate = urlTemplate; + this.width = width; + this.height = height; + } + + @Override + public synchronized URL getTileUrl(int x, int y, int zoom) { + if(AirMapWMSTile.this.maximumZ > 0 && zoom > maximumZ) { + return null; + } + if(AirMapWMSTile.this.minimumZ > 0 && zoom < minimumZ) { + return null; + } + double[] bb = getBoundingBox(x, y, zoom); + String s = this.urlTemplate + .replace("{minX}", Double.toString(bb[0])) + .replace("{minY}", Double.toString(bb[1])) + .replace("{maxX}", Double.toString(bb[2])) + .replace("{maxY}", Double.toString(bb[3])) + .replace("{width}", Integer.toString(width)) + .replace("{height}", Integer.toString(height)); + URL url = null; + try { + url = new URL(s); + } catch (MalformedURLException e) { + throw new AssertionError(e); + } + return url; + } + + private double[] getBoundingBox(int x, int y, int zoom) { + double tile = FULL / Math.pow(2, zoom); + return new double[]{ + mapBound[0] + x * tile, + mapBound[1] - (y + 1) * tile, + mapBound[0] + (x + 1) * tile, + mapBound[1] - y * tile + }; + } + + public void setUrlTemplate(String urlTemplate) { + this.urlTemplate = urlTemplate; + } + } + + private TileOverlayOptions tileOverlayOptions; + private TileOverlay tileOverlay; + private AIRMapGSUrlTileProvider tileProvider; + + private String urlTemplate; + private float zIndex; + private float maximumZ; + private float minimumZ; + private int tileSize; + private float opacity; + + public AirMapWMSTile(Context context) { + super(context); + } + + public void setUrlTemplate(String urlTemplate) { + this.urlTemplate = urlTemplate; + if (tileProvider != null) { + tileProvider.setUrlTemplate(urlTemplate); + } + if (tileOverlay != null) { + tileOverlay.clearTileCache(); + } + } + + public void setZIndex(float zIndex) { + this.zIndex = zIndex; + if (tileOverlay != null) { + tileOverlay.setZIndex(zIndex); + } + } + + public void setMaximumZ(float maximumZ) { + this.maximumZ = maximumZ; + if (tileOverlay != null) { + tileOverlay.clearTileCache(); + } + } + + public void setMinimumZ(float minimumZ) { + this.minimumZ = minimumZ; + if (tileOverlay != null) { + tileOverlay.clearTileCache(); + } + } + public void setTileSize(int tileSize) { + this.tileSize = tileSize; + if (tileOverlay != null) { + tileOverlay.clearTileCache(); + } + } + public void setOpacity(float opacity) { + this.opacity = opacity; + if (tileOverlay != null) { + tileOverlay.setTransparency(1-opacity); + } + } + + public TileOverlayOptions getTileOverlayOptions() { + if (tileOverlayOptions == null) { + tileOverlayOptions = createTileOverlayOptions(); + } + return tileOverlayOptions; + } + + private TileOverlayOptions createTileOverlayOptions() { + TileOverlayOptions options = new TileOverlayOptions(); + options.zIndex(zIndex); + options.transparency(1-opacity); + this.tileProvider = new AIRMapGSUrlTileProvider(this.tileSize, this.tileSize, this.urlTemplate); + options.tileProvider(this.tileProvider); + return options; + } + + @Override + public Object getFeature() { + return tileOverlay; + } + + @Override + public void addToMap(GoogleMap map) { + this.tileOverlay = map.addTileOverlay(getTileOverlayOptions()); + } + + @Override + public void removeFromMap(GoogleMap map) { + tileOverlay.remove(); + } +} diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapWMSTileManager.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapWMSTileManager.java new file mode 100644 index 000000000..1e7fba5cf --- /dev/null +++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapWMSTileManager.java @@ -0,0 +1,67 @@ +package com.airbnb.android.react.maps; + +import android.content.Context; +import android.os.Build; +import android.util.DisplayMetrics; +import android.view.WindowManager; + +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.annotations.ReactProp; + +public class AirMapWMSTileManager extends ViewGroupManager { + private DisplayMetrics metrics; + + public AirMapWMSTileManager(ReactApplicationContext reactContext) { + super(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + metrics = new DisplayMetrics(); + ((WindowManager) reactContext.getSystemService(Context.WINDOW_SERVICE)) + .getDefaultDisplay() + .getRealMetrics(metrics); + } else { + metrics = reactContext.getResources().getDisplayMetrics(); + } + } + + @Override + public String getName() { + return "AIRMapWMSTile"; + } + + @Override + public AirMapWMSTile createViewInstance(ThemedReactContext context) { + return new AirMapWMSTile(context); + } + + @ReactProp(name = "urlTemplate") + public void setUrlTemplate(AirMapWMSTile view, String urlTemplate) { + view.setUrlTemplate(urlTemplate); + } + + @ReactProp(name = "zIndex", defaultFloat = -1.0f) + public void setZIndex(AirMapWMSTile view, float zIndex) { + view.setZIndex(zIndex); + } + + @ReactProp(name = "minimumZ", defaultFloat = 0.0f) + public void setMinimumZ(AirMapWMSTile view, float minimumZ) { + view.setMinimumZ(minimumZ); + } + + @ReactProp(name = "maximumZ", defaultFloat = 100.0f) + public void setMaximumZ(AirMapWMSTile view, float maximumZ) { + view.setMaximumZ(maximumZ); + } + + @ReactProp(name = "tileSize", defaultInt = 512) + public void setTileSize(AirMapWMSTile view, int tileSize) { + view.setTileSize(tileSize); + } + + @ReactProp(name = "opacity", defaultFloat = 1.0f) + public void setOpacity(AirMapWMSTile view, float opacity) { + view.setOpacity(opacity); + } +} diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/MapsPackage.java b/lib/android/src/main/java/com/airbnb/android/react/maps/MapsPackage.java index 9a9bd4e62..826ee0faa 100644 --- a/lib/android/src/main/java/com/airbnb/android/react/maps/MapsPackage.java +++ b/lib/android/src/main/java/com/airbnb/android/react/maps/MapsPackage.java @@ -39,6 +39,7 @@ public List createViewManagers(ReactApplicationContext reactContext AirMapManager mapManager = new AirMapManager(reactContext); AirMapLiteManager mapLiteManager = new AirMapLiteManager(reactContext); AirMapUrlTileManager urlTileManager = new AirMapUrlTileManager(reactContext); + AirMapWMSTileManager gsUrlTileManager = new AirMapWMSTileManager(reactContext); AirMapLocalTileManager localTileManager = new AirMapLocalTileManager(reactContext); AirMapOverlayManager overlayManager = new AirMapOverlayManager(reactContext); mapManager.setMarkerManager(annotationManager); @@ -52,6 +53,7 @@ public List createViewManagers(ReactApplicationContext reactContext mapManager, mapLiteManager, urlTileManager, + gsUrlTileManager, localTileManager, overlayManager ); diff --git a/lib/components/MapView.js b/lib/components/MapView.js index d62c189cb..3123d647f 100644 --- a/lib/components/MapView.js +++ b/lib/components/MapView.js @@ -19,6 +19,7 @@ import MapCallout from './MapCallout'; import MapCalloutSubview from './MapCalloutSubview'; import MapOverlay from './MapOverlay'; import MapUrlTile from './MapUrlTile'; +import MapWMSTile from './MapWMSTile'; import MapLocalTile from './MapLocalTile'; import AnimatedRegion from './AnimatedRegion'; import { @@ -1001,6 +1002,7 @@ MapView.Polyline = MapPolyline; MapView.Polygon = MapPolygon; MapView.Circle = MapCircle; MapView.UrlTile = MapUrlTile; +MapView.MapWMSTile = MapWMSTile; MapView.LocalTile = MapLocalTile; MapView.Overlay = MapOverlay; MapView.Callout = MapCallout; diff --git a/lib/components/MapWMSTile.js b/lib/components/MapWMSTile.js new file mode 100644 index 000000000..8a6c01070 --- /dev/null +++ b/lib/components/MapWMSTile.js @@ -0,0 +1,87 @@ +import PropTypes from 'prop-types'; +import React from 'react'; + +import { + ViewPropTypes, + View, +} from 'react-native'; + +import decorateMapComponent, { + USES_DEFAULT_IMPLEMENTATION, + SUPPORTED, +} from './decorateMapComponent'; + +// if ViewPropTypes is not defined fall back to View.propType (to support RN < 0.44) +const viewPropTypes = ViewPropTypes || View.propTypes; + +const propTypes = { + ...viewPropTypes, + + /** + * The url template of the tile server. The patterns {minX} {maxX} {minY} {maxY} {width} {height} + * will be replaced at runtime according to EPSG:900913 specification bounding box. + * For example, https://demo.geo-solutions.it/geoserver/tiger/wms?service=WMS&version=1.1.0&request=GetMap&layers=tiger:poi&styles=&bbox={minX},{minY},{maxX},{maxY}&width={width}&height={height}&srs=EPSG:900913&format=image/png&transparent=true&format_options=dpi:213 + */ + urlTemplate: PropTypes.string.isRequired, + + /** + * The order in which this tile overlay is drawn with respect to other overlays. An overlay + * with a larger z-index is drawn over overlays with smaller z-indices. The order of overlays + * with the same z-index is arbitrary. The default zIndex is -1. + * + * @platform android + */ + zIndex: PropTypes.number, + /** + * The maximum zoom level for this tile overlay. + * + */ + maximumZ: PropTypes.number, + + /** + * The minimum zoom level for this tile overlay. + * + */ + minimumZ: PropTypes.number, + + /** + * Corresponds to MKTileOverlay canReplaceMapContent. + * + * @platform ios + */ + shouldReplaceMapContent: PropTypes.bool, + /** + * tileSize. + * + */ + tileSize: PropTypes.number, + + /** + * opacity. between 0 - 1 + * + */ + opacity: PropTypes.number, + +}; + +class MapWMSTile extends React.Component { + render() { + const AIRMapWMSTile = this.getAirComponent(); + return ( + + ); + } +} + +MapWMSTile.propTypes = propTypes; +export default decorateMapComponent(MapWMSTile, { + componentType: 'WMSTile', + providers: { + google: { + ios: SUPPORTED, + android: USES_DEFAULT_IMPLEMENTATION, + }, + }, +}); diff --git a/lib/ios/AirGoogleMaps/AIRGoogleMap.m b/lib/ios/AirGoogleMaps/AIRGoogleMap.m index babeb14aa..952fec2b5 100644 --- a/lib/ios/AirGoogleMaps/AIRGoogleMap.m +++ b/lib/ios/AirGoogleMaps/AIRGoogleMap.m @@ -14,6 +14,7 @@ #import "AIRGoogleMapPolyline.h" #import "AIRGoogleMapCircle.h" #import "AIRGoogleMapUrlTile.h" +#import "AIRGoogleMapWMSTile.h" #import "AIRGoogleMapOverlay.h" #import #import @@ -142,6 +143,10 @@ - (void)insertReactSubview:(id)subview atIndex:(NSInteger)atIndex AIRGoogleMapUrlTile *tile = (AIRGoogleMapUrlTile*)subview; tile.tileLayer.map = self; [self.tiles addObject:tile]; + } else if ([subview isKindOfClass:[AIRGoogleMapWMSTile class]]) { + AIRGoogleMapWMSTile *tile = (AIRGoogleMapWMSTile*)subview; + tile.tileLayer.map = self; + [self.tiles addObject:tile]; } else if ([subview isKindOfClass:[AIRGoogleMapOverlay class]]) { AIRGoogleMapOverlay *overlay = (AIRGoogleMapOverlay*)subview; overlay.overlay.map = self; @@ -182,6 +187,10 @@ - (void)removeReactSubview:(id)subview { AIRGoogleMapUrlTile *tile = (AIRGoogleMapUrlTile*)subview; tile.tileLayer.map = nil; [self.tiles removeObject:tile]; + } else if ([subview isKindOfClass:[AIRGoogleMapWMSTile class]]) { + AIRGoogleMapWMSTile *tile = (AIRGoogleMapWMSTile*)subview; + tile.tileLayer.map = nil; + [self.tiles removeObject:tile]; } else if ([subview isKindOfClass:[AIRGoogleMapOverlay class]]) { AIRGoogleMapOverlay *overlay = (AIRGoogleMapOverlay*)subview; overlay.overlay.map = nil; diff --git a/lib/ios/AirGoogleMaps/AIRGoogleMapWMSTile.h b/lib/ios/AirGoogleMaps/AIRGoogleMapWMSTile.h new file mode 100644 index 000000000..ad2acb549 --- /dev/null +++ b/lib/ios/AirGoogleMaps/AIRGoogleMapWMSTile.h @@ -0,0 +1,33 @@ +// +// AIRGoogleMapWMSTile.h +// AirMaps +// +// Created by nizam on 10/28/18. +// Copyright © 2018. All rights reserved. +// + +#ifdef HAVE_GOOGLE_MAPS + +#import +#import + +@interface WMSTileOverlay : GMSSyncTileLayer +@property (nonatomic) double MapX,MapY,FULL; +@property (nonatomic, strong) NSString *template; +@property (nonatomic, assign) NSInteger maximumZ; +@property (nonatomic, assign) NSInteger minimumZ; +@end + +@interface AIRGoogleMapWMSTile : UIView +@property (nonatomic, strong) WMSTileOverlay *tileLayer; +@property (nonatomic, assign) NSString *urlTemplate; +@property (nonatomic, assign) int zIndex; +@property (nonatomic, assign) NSInteger maximumZ; +@property (nonatomic, assign) NSInteger minimumZ; +@property (nonatomic, assign) NSInteger tileSize; +@property (nonatomic, assign) float opacity; +@end + +#endif + + diff --git a/lib/ios/AirGoogleMaps/AIRGoogleMapWMSTile.m b/lib/ios/AirGoogleMaps/AIRGoogleMapWMSTile.m new file mode 100644 index 000000000..ce1111e07 --- /dev/null +++ b/lib/ios/AirGoogleMaps/AIRGoogleMapWMSTile.m @@ -0,0 +1,125 @@ +// +// AIRGoogleMapWMSTile.m +// AirMaps +// +// Created by nizam on 10/28/18. +// Copyright © 2018. All rights reserved. +// + +#ifdef HAVE_GOOGLE_MAPS + +#import "AIRGoogleMapWMSTile.h" + +@implementation AIRGoogleMapWMSTile + +-(id) init +{ + self = [super init]; + _opacity = 1; + return self ; +} + +- (void)setZIndex:(int)zIndex +{ + _zIndex = zIndex; + _tileLayer.zIndex = zIndex; +} +- (void)setTileSize:(NSInteger)tileSize +{ + _tileSize = tileSize; + if(self.tileLayer) { + self.tileLayer.tileSize = tileSize; + [self.tileLayer clearTileCache]; + } +} +- (void)setMinimumZ:(NSInteger)minimumZ +{ + _minimumZ = minimumZ; + if(self.tileLayer && _minimumZ) { + [self.tileLayer setMinimumZ: _minimumZ ]; + [self.tileLayer clearTileCache]; + } +} + +- (void)setMaximumZ:(NSInteger)maximumZ +{ + _maximumZ = maximumZ; + if(self.tileLayer && maximumZ) { + [self.tileLayer setMaximumZ: _maximumZ ]; + [self.tileLayer clearTileCache]; + } +} +- (void)setOpacity:(float)opacity +{ + _opacity = opacity; + if(self.tileLayer ) { + [self.tileLayer setOpacity:opacity]; + [self.tileLayer clearTileCache]; + } +} + +- (void)setUrlTemplate:(NSString *)urlTemplate +{ + _urlTemplate = urlTemplate; + WMSTileOverlay *tile = [[WMSTileOverlay alloc] init]; + [tile setTemplate:urlTemplate]; + [tile setMaximumZ: _maximumZ]; + [tile setMinimumZ: _minimumZ]; + [tile setOpacity: _opacity]; + [tile setTileSize: _tileSize]; + [tile setZIndex: _zIndex]; + _tileLayer = tile; +} +@end + +@implementation WMSTileOverlay +-(id) init +{ + self = [super init]; + _MapX = -20037508.34789244; + _MapY = 20037508.34789244; + _FULL = 20037508.34789244 * 2; + return self ; +} + +-(NSArray *)getBoundBox:(NSInteger)x yAxis:(NSInteger)y zoom:(NSInteger)zoom +{ + double tile = _FULL / pow(2.0, (double)zoom); + NSArray *result =[[NSArray alloc] initWithObjects: + [NSNumber numberWithDouble:_MapX + (double)x * tile ], + [NSNumber numberWithDouble:_MapY - (double)(y+1) * tile ], + [NSNumber numberWithDouble:_MapX + (double)(x+1) * tile ], + [NSNumber numberWithDouble:_MapY - (double)y * tile ], + nil]; + + return result; + +} + +- (UIImage *)tileForX:(NSUInteger)x y:(NSUInteger)y zoom:(NSUInteger)zoom +{ + NSInteger maximumZ = self.maximumZ; + NSInteger minimumZ = self.minimumZ; + if(maximumZ && (long)zoom > (long)maximumZ) { + return nil; + } + if(minimumZ && (long)zoom < (long)minimumZ) { + return nil; + } + NSArray *bb = [self getBoundBox:x yAxis:y zoom:zoom]; + NSMutableString *url = [self.template mutableCopy]; + [url replaceOccurrencesOfString: @"{minX}" withString:[NSString stringWithFormat:@"%@", bb[0]] options:0 range:NSMakeRange(0, url.length)]; + [url replaceOccurrencesOfString: @"{minY}" withString:[NSString stringWithFormat:@"%@", bb[1]] options:0 range:NSMakeRange(0, url.length)]; + [url replaceOccurrencesOfString: @"{maxX}" withString:[NSString stringWithFormat:@"%@", bb[2]] options:0 range:NSMakeRange(0, url.length)]; + [url replaceOccurrencesOfString: @"{maxY}" withString:[NSString stringWithFormat:@"%@", bb[3]] options:0 range:NSMakeRange(0, url.length)]; + [url replaceOccurrencesOfString: @"{width}" withString:[NSString stringWithFormat:@"%d", (int)self.tileSize] options:0 range:NSMakeRange(0, url.length)]; + [url replaceOccurrencesOfString: @"{height}" withString:[NSString stringWithFormat:@"%d", (int)self.tileSize] options:0 range:NSMakeRange(0, url.length)]; + NSURL *uri = [NSURL URLWithString:url]; + NSData *data = [NSData dataWithContentsOfURL:uri]; + UIImage *img = [[UIImage alloc] initWithData:data]; + return img; +} + +@end + +#endif diff --git a/lib/ios/AirGoogleMaps/AIRGoogleMapWMSTileManager.h b/lib/ios/AirGoogleMaps/AIRGoogleMapWMSTileManager.h new file mode 100644 index 000000000..740153328 --- /dev/null +++ b/lib/ios/AirGoogleMaps/AIRGoogleMapWMSTileManager.h @@ -0,0 +1,17 @@ +// +// AIRGoogleMapWMSTileManager.h +// AirMaps +// +// Created by nizam on 10/28/18. +// Copyright © 2018. All rights reserved. +// + +#ifdef HAVE_GOOGLE_MAPS + +#import +#import + +@interface AIRGoogleMapWMSTileManager : RCTViewManager +@end + +#endif diff --git a/lib/ios/AirGoogleMaps/AIRGoogleMapWMSTileManager.m b/lib/ios/AirGoogleMaps/AIRGoogleMapWMSTileManager.m new file mode 100644 index 000000000..c2f668565 --- /dev/null +++ b/lib/ios/AirGoogleMaps/AIRGoogleMapWMSTileManager.m @@ -0,0 +1,37 @@ +// +// AIRGoogleMapWMSTileManager.m +// AirMaps +// +// Created by nizam on 10/28/18. +// Copyright © 2018. All rights reserved. +// + +#ifdef HAVE_GOOGLE_MAPS + +#import "AIRGoogleMapWMSTileManager.h" +#import "AIRGoogleMapWMSTile.h" + +@interface AIRGoogleMapWMSTileManager() + +@end + +@implementation AIRGoogleMapWMSTileManager + +RCT_EXPORT_MODULE() + +- (UIView *)view +{ + AIRGoogleMapWMSTile *tileLayer = [AIRGoogleMapWMSTile new]; + return tileLayer; +} + +RCT_EXPORT_VIEW_PROPERTY(urlTemplate, NSString) +RCT_EXPORT_VIEW_PROPERTY(zIndex, int) +RCT_EXPORT_VIEW_PROPERTY(maximumZ, int) +RCT_EXPORT_VIEW_PROPERTY(minimumZ, int) +RCT_EXPORT_VIEW_PROPERTY(tileSize, int) +RCT_EXPORT_VIEW_PROPERTY(opacity, float) + +@end + +#endif diff --git a/lib/ios/AirMaps.xcodeproj/project.pbxproj b/lib/ios/AirMaps.xcodeproj/project.pbxproj index 4a0b197ef..18e9003a9 100644 --- a/lib/ios/AirMaps.xcodeproj/project.pbxproj +++ b/lib/ios/AirMaps.xcodeproj/project.pbxproj @@ -52,6 +52,10 @@ 9B9498DA2017EFB800158761 /* AIRGoogleMapPolygon.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B9498C62017EFB800158761 /* AIRGoogleMapPolygon.m */; }; 9B9498DB2017EFB800158761 /* AIRGoogleMapMarker.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B9498C72017EFB800158761 /* AIRGoogleMapMarker.m */; }; 9B9498DC2017EFB800158761 /* AIRGoogleMapPolygonManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B9498C92017EFB800158761 /* AIRGoogleMapPolygonManager.m */; }; + A8494E28218891020092506D /* AIRMapWMSTileManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A8494E24218891020092506D /* AIRMapWMSTileManager.m */; }; + A8494E29218891020092506D /* AIRMapWMSTile.m in Sources */ = {isa = PBXBuildFile; fileRef = A8494E27218891020092506D /* AIRMapWMSTile.m */; }; + A8494E2E218891180092506D /* AIRGoogleMapWMSTileManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A8494E2A218891180092506D /* AIRGoogleMapWMSTileManager.m */; }; + A8494E2F218891180092506D /* AIRGoogleMapWMSTile.m in Sources */ = {isa = PBXBuildFile; fileRef = A8494E2D218891180092506D /* AIRGoogleMapWMSTile.m */; }; B5EA3BA92098E22B000E7AFD /* AIRDummyView.m in Sources */ = {isa = PBXBuildFile; fileRef = B5EA3BA72098E22B000E7AFD /* AIRDummyView.m */; }; DA6C26381C9E2AFE0035349F /* AIRMapUrlTile.m in Sources */ = {isa = PBXBuildFile; fileRef = DA6C26371C9E2AFE0035349F /* AIRMapUrlTile.m */; }; DA6C263E1C9E324A0035349F /* AIRMapUrlTileManager.m in Sources */ = {isa = PBXBuildFile; fileRef = DA6C263D1C9E324A0035349F /* AIRMapUrlTileManager.m */; }; @@ -123,8 +127,8 @@ 8B19A3C62257BBDF00BB8735 /* AIRMapCalloutSubview.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AIRMapCalloutSubview.h; sourceTree = ""; }; 8B19A3C72257BBDF00BB8735 /* AIRMapCalloutSubviewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AIRMapCalloutSubviewManager.m; sourceTree = ""; }; 8BC85FAD2107C0BD0006CEA5 /* User.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = User.xcconfig; sourceTree = ""; }; - 8BC85FAE2107CFD80006CEA5 /* AIRGoogleMapOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIRGoogleMapOverlay.h; path = "AirGoogleMaps/AIRGoogleMapOverlay.h"; sourceTree = ""; }; - 8BC85FAF2107CFEC0006CEA5 /* AIRGoogleMapOverlay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIRGoogleMapOverlay.m; path = "AirGoogleMaps/AIRGoogleMapOverlay.m"; sourceTree = ""; }; + 8BC85FAE2107CFD80006CEA5 /* AIRGoogleMapOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIRGoogleMapOverlay.h; path = AirGoogleMaps/AIRGoogleMapOverlay.h; sourceTree = ""; }; + 8BC85FAF2107CFEC0006CEA5 /* AIRGoogleMapOverlay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIRGoogleMapOverlay.m; path = AirGoogleMaps/AIRGoogleMapOverlay.m; sourceTree = ""; }; 9B9498A42017EFB400158761 /* AIRGoogleMapCallout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIRGoogleMapCallout.h; path = AirGoogleMaps/AIRGoogleMapCallout.h; sourceTree = ""; }; 9B9498A52017EFB400158761 /* AIRGoogleMapPolygonManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIRGoogleMapPolygonManager.h; path = AirGoogleMaps/AIRGoogleMapPolygonManager.h; sourceTree = ""; }; 9B9498A62017EFB400158761 /* AIRGoogleMapUrlTile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIRGoogleMapUrlTile.m; path = AirGoogleMaps/AIRGoogleMapUrlTile.m; sourceTree = ""; }; @@ -161,6 +165,14 @@ 9B9498C72017EFB800158761 /* AIRGoogleMapMarker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIRGoogleMapMarker.m; path = AirGoogleMaps/AIRGoogleMapMarker.m; sourceTree = ""; }; 9B9498C82017EFB800158761 /* AIRGoogleMapUrlTile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIRGoogleMapUrlTile.h; path = AirGoogleMaps/AIRGoogleMapUrlTile.h; sourceTree = ""; }; 9B9498C92017EFB800158761 /* AIRGoogleMapPolygonManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIRGoogleMapPolygonManager.m; path = AirGoogleMaps/AIRGoogleMapPolygonManager.m; sourceTree = ""; }; + A8494E24218891020092506D /* AIRMapWMSTileManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AIRMapWMSTileManager.m; sourceTree = ""; }; + A8494E25218891020092506D /* AIRMapWMSTile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AIRMapWMSTile.h; sourceTree = ""; }; + A8494E26218891020092506D /* AIRMapWMSTileManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AIRMapWMSTileManager.h; sourceTree = ""; }; + A8494E27218891020092506D /* AIRMapWMSTile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AIRMapWMSTile.m; sourceTree = ""; }; + A8494E2A218891180092506D /* AIRGoogleMapWMSTileManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIRGoogleMapWMSTileManager.m; path = AirGoogleMaps/AIRGoogleMapWMSTileManager.m; sourceTree = ""; }; + A8494E2B218891180092506D /* AIRGoogleMapWMSTile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIRGoogleMapWMSTile.h; path = AirGoogleMaps/AIRGoogleMapWMSTile.h; sourceTree = ""; }; + A8494E2C218891180092506D /* AIRGoogleMapWMSTileManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIRGoogleMapWMSTileManager.h; path = AirGoogleMaps/AIRGoogleMapWMSTileManager.h; sourceTree = ""; }; + A8494E2D218891180092506D /* AIRGoogleMapWMSTile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIRGoogleMapWMSTile.m; path = AirGoogleMaps/AIRGoogleMapWMSTile.m; sourceTree = ""; }; B5EA3BA72098E22B000E7AFD /* AIRDummyView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIRDummyView.m; path = AirGoogleMaps/AIRDummyView.m; sourceTree = ""; }; B5EA3BA82098E22B000E7AFD /* AIRDummyView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIRDummyView.h; path = AirGoogleMaps/AIRDummyView.h; sourceTree = ""; }; DA6C26361C9E2AFE0035349F /* AIRMapUrlTile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AIRMapUrlTile.h; sourceTree = ""; }; @@ -242,6 +254,11 @@ DA6C26371C9E2AFE0035349F /* AIRMapUrlTile.m */, DA6C263C1C9E324A0035349F /* AIRMapUrlTileManager.h */, DA6C263D1C9E324A0035349F /* AIRMapUrlTileManager.m */, + A8494E25218891020092506D /* AIRMapWMSTile.h */, + A8494E27218891020092506D /* AIRMapWMSTile.m */, + A8494E26218891020092506D /* AIRMapWMSTileManager.h */, + A8494E24218891020092506D /* AIRMapWMSTileManager.m */, + 1125B2CC1C4AD3DA007D0023 /* AIRMapMarker.m */, 628F811E1FD16D780058313A /* AIRMapLocalTile.h */, 628F811F1FD16DF80058313A /* AIRMapLocalTile.m */, 628F81211FD16EAB0058313A /* AIRMapLocalTileManager.h */, @@ -301,6 +318,10 @@ 9B9498A62017EFB400158761 /* AIRGoogleMapUrlTile.m */, 9B9498AD2017EFB400158761 /* AIRGoogleMapUrlTileManager.h */, 9B9498A72017EFB400158761 /* AIRGoogleMapURLTileManager.m */, + A8494E2B218891180092506D /* AIRGoogleMapWMSTile.h */, + A8494E2D218891180092506D /* AIRGoogleMapWMSTile.m */, + A8494E2C218891180092506D /* AIRGoogleMapWMSTileManager.h */, + A8494E2A218891180092506D /* AIRGoogleMapWMSTileManager.m */, 9B9498A92017EFB400158761 /* RCTConvert+GMSMapViewType.h */, 9B9498B92017EFB600158761 /* RCTConvert+GMSMapViewType.m */, ); @@ -368,7 +389,9 @@ 1125B2E31C4AD3DA007D0023 /* AIRMapPolygon.m in Sources */, 4C99C9DE2226CF2800A8693E /* AIRWeakTimerReference.m in Sources */, 1125B2E41C4AD3DA007D0023 /* AIRMapPolygonManager.m in Sources */, + A8494E2F218891180092506D /* AIRGoogleMapWMSTile.m in Sources */, 9B9498CB2017EFB800158761 /* AIRGoogleMapURLTileManager.m in Sources */, + A8494E28218891020092506D /* AIRMapWMSTileManager.m in Sources */, 1125B2DB1C4AD3DA007D0023 /* AIRMapCallout.m in Sources */, 53D31636202E723B00B55447 /* AIRMapOverlayManager.m in Sources */, 1125B2E01C4AD3DA007D0023 /* AIRMapManager.m in Sources */, @@ -379,6 +402,7 @@ 9B9498CE2017EFB800158761 /* AIRGMSMarker.m in Sources */, 9B9498D72017EFB800158761 /* AIRGoogleMapManager.m in Sources */, 19DABC7F1E7C9D3C00F41150 /* RCTConvert+AirMap.m in Sources */, + A8494E2E218891180092506D /* AIRGoogleMapWMSTileManager.m in Sources */, 8B19A3C82257BBDF00BB8735 /* AIRMapCalloutSubview.m in Sources */, 1125B2E51C4AD3DA007D0023 /* AIRMapPolyline.m in Sources */, 4C99C9E12226D8C400A8693E /* AIRWeakMapReference.m in Sources */, @@ -400,6 +424,7 @@ 2163AA501FEAEDD100BBEC95 /* AIRMapPolylineRenderer.m in Sources */, 9B9498D02017EFB800158761 /* AIRGoogleMapPolylineManager.m in Sources */, 1125B2E11C4AD3DA007D0023 /* AIRMapMarker.m in Sources */, + A8494E29218891020092506D /* AIRMapWMSTile.m in Sources */, 9B9498CA2017EFB800158761 /* AIRGoogleMapUrlTile.m in Sources */, 8B19A3C92257BBDF00BB8735 /* AIRMapCalloutSubviewManager.m in Sources */, B5EA3BA92098E22B000E7AFD /* AIRDummyView.m in Sources */, diff --git a/lib/ios/AirMaps/AIRMap.m b/lib/ios/AirMaps/AIRMap.m index 59b684047..dc3bc0978 100644 --- a/lib/ios/AirMaps/AIRMap.m +++ b/lib/ios/AirMaps/AIRMap.m @@ -17,6 +17,7 @@ #import "AIRMapCircle.h" #import #import "AIRMapUrlTile.h" +#import "AIRMapWMSTile.h" #import "AIRMapLocalTile.h" #import "AIRMapOverlay.h" @@ -122,6 +123,9 @@ - (void)insertReactSubview:(id)subview atIndex:(NSInteger)atIndex } else if ([subview isKindOfClass:[AIRMapUrlTile class]]) { ((AIRMapUrlTile *)subview).map = self; [self addOverlay:(id)subview]; + }else if ([subview isKindOfClass:[AIRMapWMSTile class]]) { + ((AIRMapWMSTile *)subview).map = self; + [self addOverlay:(id)subview]; } else if ([subview isKindOfClass:[AIRMapLocalTile class]]) { ((AIRMapLocalTile *)subview).map = self; [self addOverlay:(id)subview]; diff --git a/lib/ios/AirMaps/AIRMapManager.m b/lib/ios/AirMaps/AIRMapManager.m index 7588ba163..33dbd20b2 100644 --- a/lib/ios/AirMaps/AIRMapManager.m +++ b/lib/ios/AirMaps/AIRMapManager.m @@ -23,6 +23,7 @@ #import "AIRMapCircle.h" #import "SMCalloutView.h" #import "AIRMapUrlTile.h" +#import "AIRMapWMSTile.h" #import "AIRMapLocalTile.h" #import "AIRMapSnapshot.h" #import "RCTConvert+AirMap.h" @@ -783,6 +784,8 @@ - (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id +#import +#import + +#import +#import +#import "AIRMapCoordinate.h" +#import "AIRMap.h" +#import "RCTConvert+AirMap.h" + +@interface AIRMapWMSTile : MKAnnotationView + +@property (nonatomic, weak) AIRMap *map; + +@property (nonatomic, strong) MKTileOverlay *tileOverlay; +@property (nonatomic, strong) MKTileOverlayRenderer *renderer; +@property (nonatomic, copy) NSString *urlTemplate; +@property NSInteger maximumZ; +@property NSInteger minimumZ; +@property NSInteger tileSize; +@property BOOL shouldReplaceMapContent; + +#pragma mark MKOverlay protocol + +@property(nonatomic, readonly) CLLocationCoordinate2D coordinate; +@property(nonatomic, readonly) MKMapRect boundingMapRect; +- (BOOL)canReplaceMapContent; +@end + +@interface TileOverlay : MKTileOverlay +@property (nonatomic) double MapX,MapY,FULL; +@end diff --git a/lib/ios/AirMaps/AIRMapWMSTile.m b/lib/ios/AirMaps/AIRMapWMSTile.m new file mode 100644 index 000000000..c864b4c97 --- /dev/null +++ b/lib/ios/AirMaps/AIRMapWMSTile.m @@ -0,0 +1,149 @@ +// +// AIRMapWMSTile.m +// AirMaps +// +// Created by nizam on 10/28/18. +// Copyright © 2018. All rights reserved. +// + +#import "AIRMapWMSTile.h" +#import + +@implementation AIRMapWMSTile { + BOOL _urlTemplateSet; +} + +- (void)setShouldReplaceMapContent:(BOOL)shouldReplaceMapContent +{ + _shouldReplaceMapContent = shouldReplaceMapContent; + if(self.tileOverlay) { + self.tileOverlay.canReplaceMapContent = _shouldReplaceMapContent; + } + [self update]; +} + +- (void)setMaximumZ:(NSUInteger)maximumZ +{ + _maximumZ = maximumZ; + if(self.tileOverlay) { + self.tileOverlay.maximumZ = _maximumZ; + } + [self update]; +} + +- (void)setMinimumZ:(NSUInteger)minimumZ +{ + _minimumZ = minimumZ; + if(self.tileOverlay) { + self.tileOverlay.minimumZ = _minimumZ; + } + [self update]; +} + +- (void)setTileSize:(NSInteger)tileSize +{ + _tileSize = tileSize; + if(self.tileOverlay) { + self.tileOverlay.tileSize = CGSizeMake(tileSize, tileSize); + } + [self update]; +} +- (void)setUrlTemplate:(NSString *)urlTemplate{ + _urlTemplate = urlTemplate; + _urlTemplateSet = YES; + [self createTileOverlayAndRendererIfPossible]; + [self update]; +} + +- (void) createTileOverlayAndRendererIfPossible +{ + if (!_urlTemplateSet) return; + self.tileOverlay = [[TileOverlay alloc] initWithURLTemplate:self.urlTemplate]; + self.tileOverlay.canReplaceMapContent = self.shouldReplaceMapContent; + + if(self.minimumZ) { + self.tileOverlay.minimumZ = self.minimumZ; + } + if (self.maximumZ) { + self.tileOverlay.maximumZ = self.maximumZ; + } + if (self.tileSize) { + self.tileOverlay.tileSize = CGSizeMake(self.tileSize, self.tileSize);; + } + self.renderer = [[MKTileOverlayRenderer alloc] initWithTileOverlay:self.tileOverlay]; +} + +- (void) update +{ + if (!_renderer) return; + + if (_map == nil) return; + [_map removeOverlay:self]; + [_map addOverlay:self level:MKOverlayLevelAboveLabels]; + for (id overlay in _map.overlays) { + if ([overlay isKindOfClass:[AIRMapWMSTile class]]) { + continue; + } + [_map removeOverlay:overlay]; + [_map addOverlay:overlay]; + } +} + +#pragma mark MKOverlay implementation + +- (CLLocationCoordinate2D) coordinate +{ + return self.tileOverlay.coordinate; +} + +- (MKMapRect) boundingMapRect +{ + return self.tileOverlay.boundingMapRect; +} + +- (BOOL)canReplaceMapContent +{ + return self.tileOverlay.canReplaceMapContent; +} + +@end + +@implementation TileOverlay +@synthesize MapX; +@synthesize MapY; +@synthesize FULL; + +-(id) initWithURLTemplate:(NSString *)URLTemplate { + self = [super initWithURLTemplate:URLTemplate]; + MapX = -20037508.34789244; + MapY = 20037508.34789244; + FULL = 20037508.34789244 * 2; + return self ; +} + +-(NSURL *)URLForTilePath:(MKTileOverlayPath)path{ + NSArray *bb = [self getBoundBox:path.x yAxis:path.y zoom:path.z]; + NSMutableString *url = [self.URLTemplate mutableCopy]; + [url replaceOccurrencesOfString: @"{minX}" withString:[NSString stringWithFormat:@"%@", bb[0]] options:0 range:NSMakeRange(0, url.length)]; + [url replaceOccurrencesOfString: @"{minY}" withString:[NSString stringWithFormat:@"%@", bb[1]] options:0 range:NSMakeRange(0, url.length)]; + [url replaceOccurrencesOfString: @"{maxX}" withString:[NSString stringWithFormat:@"%@", bb[2]] options:0 range:NSMakeRange(0, url.length)]; + [url replaceOccurrencesOfString: @"{maxY}" withString:[NSString stringWithFormat:@"%@", bb[3]] options:0 range:NSMakeRange(0, url.length)]; + [url replaceOccurrencesOfString: @"{width}" withString:[NSString stringWithFormat:@"%d", (int)self.tileSize.width] options:0 range:NSMakeRange(0, url.length)]; + [url replaceOccurrencesOfString: @"{height}" withString:[NSString stringWithFormat:@"%d", (int)self.tileSize.height] options:0 range:NSMakeRange(0, url.length)]; + return [NSURL URLWithString:url]; +} + +-(NSArray *)getBoundBox:(NSInteger)x yAxis:(NSInteger)y zoom:(NSInteger)zoom{ + double tile = FULL / pow(2.0, (double)zoom); + + NSArray *result =[[NSArray alloc] initWithObjects: + [NSNumber numberWithDouble:MapX + (double)x * tile ], + [NSNumber numberWithDouble:MapY - (double)(y+1) * tile ], + [NSNumber numberWithDouble:MapX + (double)(x+1) * tile ], + [NSNumber numberWithDouble:MapY - (double)y * tile ], + nil]; + + return result; + +} +@end diff --git a/lib/ios/AirMaps/AIRMapWMSTileManager.h b/lib/ios/AirMaps/AIRMapWMSTileManager.h new file mode 100644 index 000000000..5ca4a19a3 --- /dev/null +++ b/lib/ios/AirMaps/AIRMapWMSTileManager.h @@ -0,0 +1,14 @@ +// +// AIRMapWMSTileManager.h +// AirMaps +// +// Created by nizam on 10/28/18. +// Copyright © 2018. All rights reserved. +// + + +#import + +@interface AIRMapWMSTileManager : RCTViewManager + +@end diff --git a/lib/ios/AirMaps/AIRMapWMSTileManager.m b/lib/ios/AirMaps/AIRMapWMSTileManager.m new file mode 100644 index 000000000..bfbdcf759 --- /dev/null +++ b/lib/ios/AirMaps/AIRMapWMSTileManager.m @@ -0,0 +1,42 @@ +// +// AIRMapWMSTileManager.m +// AirMaps +// +// Created by nizam on 10/28/18. +// Copyright © 2018. All rights reserved. +// + + +#import +#import +#import +#import +#import +#import +#import "AIRMapMarker.h" +#import "AIRMapWMSTile.h" + +#import "AIRMapWMSTileManager.h" + +@interface AIRMapWMSTileManager() + +@end + +@implementation AIRMapWMSTileManager + + +RCT_EXPORT_MODULE() + +- (UIView *)view +{ + AIRMapWMSTile *tile = [AIRMapWMSTile new]; + return tile; +} + +RCT_EXPORT_VIEW_PROPERTY(urlTemplate, NSString) +RCT_EXPORT_VIEW_PROPERTY(maximumZ, NSInteger) +RCT_EXPORT_VIEW_PROPERTY(minimumZ, NSInteger) +RCT_EXPORT_VIEW_PROPERTY(tileSize, NSInteger) +RCT_EXPORT_VIEW_PROPERTY(shouldReplaceMapContent, BOOL) + +@end From 35e96432e7566953837441aca6b2040f5a777cb3 Mon Sep 17 00:00:00 2001 From: Daniel Dimitrov Date: Mon, 15 Apr 2019 01:57:14 +0200 Subject: [PATCH 09/27] TestID's for e2e automation (#2253) * re-applying pull request 1170 It seems that the following pull https://github.com/react-community/react-native-maps/pull/1170 request has been abandoned, but testID is still required if one wants to make e2e tests https://github.com/react-community/react-native-maps/issues/2252 * Update App.js --- example/App.js | 2 + example/examples/TestIdMarkers.js | 80 +++++++++++++++++++ lib/components/MapMarker.js | 5 ++ lib/ios/AirGoogleMaps/AIRGoogleMapManager.m | 4 +- .../AirGoogleMaps/AIRGoogleMapMarkerManager.m | 3 + 5 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 example/examples/TestIdMarkers.js diff --git a/example/App.js b/example/App.js index 5b24483d9..bec38b98a 100644 --- a/example/App.js +++ b/example/App.js @@ -44,6 +44,7 @@ import ImageOverlayWithAssets from './examples/ImageOverlayWithAssets'; import ImageOverlayWithURL from './examples/ImageOverlayWithURL'; import AnimatedNavigation from './examples/AnimatedNavigation'; import OnPoiClick from './examples/OnPoiClick'; +import TestIdMarkers from './examples/TestIdMarkers'; import IndoorMap from './examples/IndoorMap'; import CameraControl from './examples/CameraControl'; import MassiveCustomMarkers from './examples/MassiveCustomMarkers'; @@ -164,6 +165,7 @@ export default class App extends React.Component { [LegalLabel, 'Reposition the legal label', true], [SetNativePropsOverlays, 'Update native props', true], [CustomOverlay, 'Custom Overlay Component', true], + [TestIdMarkers, 'Test ID for Automation', true], [MapKml, 'Load Map with KML', true], [BugMarkerWontUpdate, 'BUG: Marker Won\'t Update (Android)', true], [ImageOverlayWithAssets, 'Image Overlay Component with Assets', true], diff --git a/example/examples/TestIdMarkers.js b/example/examples/TestIdMarkers.js new file mode 100644 index 000000000..1537992f9 --- /dev/null +++ b/example/examples/TestIdMarkers.js @@ -0,0 +1,80 @@ +import React from 'react'; +import { + StyleSheet, + View, + Dimensions, +} from 'react-native'; + +import MapView from 'react-native-maps'; + +const { width, height } = Dimensions.get('window'); + +const ASPECT_RATIO = width / height; +const LATITUDE = 37.78825; +const LONGITUDE = -122.4324; +const LATITUDE_DELTA = 0.0922; +const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; +const SPACE = 0.01; + +function log(eventName, e) { + console.log(eventName, e.nativeEvent); +} + +class MarkerTypes extends React.Component { + constructor(props) { + super(props); + + this.state = { + a: { + latitude: LATITUDE + SPACE, + longitude: LONGITUDE + SPACE, + }, + }; + } + + render() { + return ( + + + log('onSelect', e)} + onDrag={(e) => log('onDrag', e)} + onDragStart={(e) => log('onDragStart', e)} + onDragEnd={(e) => log('onDragEnd', e)} + onPress={(e) => log('onPress', e)} + draggable + /> + + + ); + } +} + +MarkerTypes.propTypes = { + provider: MapView.ProviderPropType, +}; + +const styles = StyleSheet.create({ + container: { + ...StyleSheet.absoluteFillObject, + justifyContent: 'flex-end', + alignItems: 'center', + }, + map: { + ...StyleSheet.absoluteFillObject, + }, +}); + +module.exports = MarkerTypes; + diff --git a/lib/components/MapMarker.js b/lib/components/MapMarker.js index 61be174ba..6555bcb60 100644 --- a/lib/components/MapMarker.js +++ b/lib/components/MapMarker.js @@ -48,6 +48,11 @@ const propTypes = { */ description: PropTypes.string, + /** + * Test ID for end to end test automation + */ + testID: PropTypes.string, + /** * A custom image to be used as the marker's icon. Only local image resources are allowed to be * used. diff --git a/lib/ios/AirGoogleMaps/AIRGoogleMapManager.m b/lib/ios/AirGoogleMaps/AIRGoogleMapManager.m index 752ac09dc..d35d4bd5b 100644 --- a/lib/ios/AirGoogleMaps/AIRGoogleMapManager.m +++ b/lib/ios/AirGoogleMaps/AIRGoogleMapManager.m @@ -48,9 +48,11 @@ - (UIView *)view AIRGoogleMap *map = [AIRGoogleMap new]; map.bridge = self.bridge; map.delegate = self; + map.isAccessibilityElement = "YES"; + map.accessibilityElementsHidden = "NO"; + map.settings.consumesGesturesInView = NO; map.indoorDisplay.delegate = self; self.map = map; - map.settings.consumesGesturesInView = NO; UIPanGestureRecognizer *drag = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleMapDrag:)]; [drag setMinimumNumberOfTouches:1]; diff --git a/lib/ios/AirGoogleMaps/AIRGoogleMapMarkerManager.m b/lib/ios/AirGoogleMaps/AIRGoogleMapMarkerManager.m index ea27a03e9..dd04c7542 100644 --- a/lib/ios/AirGoogleMaps/AIRGoogleMapMarkerManager.m +++ b/lib/ios/AirGoogleMaps/AIRGoogleMapMarkerManager.m @@ -25,6 +25,8 @@ - (UIView *)view // tapGestureRecognizer.cancelsTouchesInView = NO; // [marker addGestureRecognizer:tapGestureRecognizer]; marker.bridge = self.bridge; + marker.isAccessibilityElement = "YES"; + marker.accessibilityElementsHidden = "NO"; return marker; } @@ -35,6 +37,7 @@ - (UIView *)view RCT_REMAP_VIEW_PROPERTY(image, imageSrc, NSString) RCT_REMAP_VIEW_PROPERTY(icon, iconSrc, NSString) RCT_EXPORT_VIEW_PROPERTY(title, NSString) +RCT_REMAP_VIEW_PROPERTY(testID, accessibilityIdentifier, NSString) RCT_REMAP_VIEW_PROPERTY(description, subtitle, NSString) RCT_EXPORT_VIEW_PROPERTY(pinColor, UIColor) RCT_EXPORT_VIEW_PROPERTY(anchor, CGPoint) From 90e4a6e3b12e21b92a635321e02f010693fca6cb Mon Sep 17 00:00:00 2001 From: Christopher Dro Date: Tue, 16 Apr 2019 13:33:09 -0700 Subject: [PATCH 10/27] [AIRGoogleMap] Free instance variable in getActionForTarget (#2815) --- lib/ios/AirGoogleMaps/AIRGoogleMap.m | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/ios/AirGoogleMaps/AIRGoogleMap.m b/lib/ios/AirGoogleMaps/AIRGoogleMap.m index 952fec2b5..7ff6dbe2a 100644 --- a/lib/ios/AirGoogleMaps/AIRGoogleMap.m +++ b/lib/ios/AirGoogleMaps/AIRGoogleMap.m @@ -619,6 +619,7 @@ -(SEL)getActionForTarget:(NSObject*)target { } } } + free(ivars); return action; } From 55a28792941c84c578ace632d787aef8aed27c3a Mon Sep 17 00:00:00 2001 From: Denis Oblogin Date: Wed, 17 Apr 2019 18:16:29 +0300 Subject: [PATCH 11/27] Weak refs to gesture targets (#2818) --- lib/ios/AirGoogleMaps/AIRGoogleMap.m | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/lib/ios/AirGoogleMaps/AIRGoogleMap.m b/lib/ios/AirGoogleMaps/AIRGoogleMap.m index 7ff6dbe2a..4292c2314 100644 --- a/lib/ios/AirGoogleMaps/AIRGoogleMap.m +++ b/lib/ios/AirGoogleMaps/AIRGoogleMap.m @@ -52,6 +52,8 @@ @interface AIRGoogleMap () - (id)eventFromCoordinate:(CLLocationCoordinate2D)coordinate; +@property (nonatomic, strong) NSMutableDictionary *origGestureRecognizersMeta; + @end @implementation AIRGoogleMap @@ -62,7 +64,6 @@ @implementation AIRGoogleMap BOOL _initialCameraSetOnLoad; BOOL _didCallOnMapReady; BOOL _didMoveToWindow; - NSMutableDictionary *_origGestureRecognizersMeta; BOOL _zoomTapEnabled; } @@ -91,7 +92,7 @@ - (instancetype)init options:NSKeyValueObservingOptionNew context:NULL]; - _origGestureRecognizersMeta = [[NSMutableDictionary alloc] init]; + self.origGestureRecognizersMeta = [[NSMutableDictionary alloc] init]; } return self; } @@ -629,7 +630,7 @@ -(void)overrideGestureRecognizersForView:(UIView*)view { NSArray* grs = view.gestureRecognizers; for (UIGestureRecognizer* gestureRecognizer in grs) { NSNumber* grHash = [NSNumber numberWithUnsignedInteger:gestureRecognizer.hash]; - if([_origGestureRecognizersMeta objectForKey:grHash] != nil) + if([self.origGestureRecognizersMeta objectForKey:grHash] != nil) continue; //already patched //get original handlers @@ -640,7 +641,10 @@ -(void)overrideGestureRecognizersForView:(UIView*)view { NSObject* target = [trg valueForKey:@"target"]; SEL action = [self getActionForTarget:trg]; isZoomTapGesture = [NSStringFromSelector(action) isEqualToString:@"handleZoomTapGesture:"]; - [origTargetsActions addObject:@{@"target": target, @"action": NSStringFromSelector(action)}]; + [origTargetsActions addObject:@{ + @"target": [NSValue valueWithNonretainedObject:target], + @"action": NSStringFromSelector(action) + }]; } if (isZoomTapGesture && self.zoomTapEnabled == NO) { [view removeGestureRecognizer:gestureRecognizer]; @@ -649,15 +653,16 @@ -(void)overrideGestureRecognizersForView:(UIView*)view { //replace with extendedMapGestureHandler for (NSDictionary* origTargetAction in origTargetsActions) { - NSObject* target = [origTargetAction objectForKey:@"target"]; + NSValue* targetValue = [origTargetAction objectForKey:@"target"]; + NSObject* target = [targetValue nonretainedObjectValue]; NSString* actionString = [origTargetAction objectForKey:@"action"]; SEL action = NSSelectorFromString(actionString); [gestureRecognizer removeTarget:target action:action]; } [gestureRecognizer addTarget:self action:@selector(extendedMapGestureHandler:)]; - [_origGestureRecognizersMeta setObject:@{@"targets": origTargetsActions} - forKey:grHash]; + [self.origGestureRecognizersMeta setObject:@{@"targets": origTargetsActions} + forKey:grHash]; } } @@ -735,10 +740,11 @@ - (id)extendedMapGestureHandler:(UIGestureRecognizer*)gestureRecognizer { } if (performOriginalActions) { - NSDictionary* origMeta = [_origGestureRecognizersMeta objectForKey:grHash]; + NSDictionary* origMeta = [self.origGestureRecognizersMeta objectForKey:grHash]; NSDictionary* origTargets = [origMeta objectForKey:@"targets"]; for (NSDictionary* origTarget in origTargets) { - NSObject* target = [origTarget objectForKey:@"target"]; + NSValue* targetValue = [origTarget objectForKey:@"target"]; + NSObject* target = [targetValue nonretainedObjectValue]; NSString* actionString = [origTarget objectForKey:@"action"]; SEL action = NSSelectorFromString(actionString); #pragma clang diagnostic push From bc62d399cb83500f0051f916ee47437c4a9f022b Mon Sep 17 00:00:00 2001 From: Alter Code Date: Thu, 18 Apr 2019 20:15:52 +0700 Subject: [PATCH 12/27] add support for loading base64 encoded image (#2392) --- .../main/java/com/airbnb/android/react/maps/AirMapMarker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapMarker.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapMarker.java index 6cc30a728..77beefd7e 100644 --- a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapMarker.java +++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapMarker.java @@ -353,7 +353,7 @@ public void setImage(String uri) { iconBitmapDescriptor = null; update(true); } else if (uri.startsWith("http://") || uri.startsWith("https://") || - uri.startsWith("file://") || uri.startsWith("asset://")) { + uri.startsWith("file://") || uri.startsWith("asset://") || uri.startsWith("data:")) { ImageRequest imageRequest = ImageRequestBuilder .newBuilderWithSource(Uri.parse(uri)) .build(); From 4d48f785e8b3d4ff0c9c37a1b4d381edf206be73 Mon Sep 17 00:00:00 2001 From: Juan Date: Sat, 20 Apr 2019 02:22:57 +0100 Subject: [PATCH 13/27] Reword Overlay documentation to match implementation (#2822) --- docs/overlay.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/overlay.md b/docs/overlay.md index 29a12aee3..a12f8b4b3 100644 --- a/docs/overlay.md +++ b/docs/overlay.md @@ -5,13 +5,13 @@ | Prop | Type | Default | Note | |---|---|---|---| | `image` | `ImageSource` | A custom image to be used as the overlay. Only required local image resources and uri (as for images located in the net) are allowed to be used. -| `bounds` | `Array` | | The coordinates for the image (left-top corner, right-bottom corner). +| `bounds` | `Array` | | The coordinates for the image (left-top corner, right-bottom corner). ie.```[[lat, long], [lat, long]]``` ## Types ``` -type LatLng { +type LatLng = [ latitude: Number, longitude: Number, -} +] ``` From a7f88e6d8fa84206980cb3842e8e4804470c1b18 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Tue, 23 Apr 2019 10:49:14 +0300 Subject: [PATCH 14/27] Bugfix/accessibility (#2826) * Fixed string passed for booleans * Added missing accessibility properties --- lib/ios/AirGoogleMaps/AIRGoogleMapManager.m | 5 +++-- lib/ios/AirGoogleMaps/AIRGoogleMapMarkerManager.m | 4 ++-- lib/ios/AirMaps/AIRMapManager.m | 4 ++++ lib/ios/AirMaps/AIRMapMarkerManager.m | 3 +++ 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/ios/AirGoogleMaps/AIRGoogleMapManager.m b/lib/ios/AirGoogleMaps/AIRGoogleMapManager.m index d35d4bd5b..9c4090f56 100644 --- a/lib/ios/AirGoogleMaps/AIRGoogleMapManager.m +++ b/lib/ios/AirGoogleMaps/AIRGoogleMapManager.m @@ -48,8 +48,8 @@ - (UIView *)view AIRGoogleMap *map = [AIRGoogleMap new]; map.bridge = self.bridge; map.delegate = self; - map.isAccessibilityElement = "YES"; - map.accessibilityElementsHidden = "NO"; + map.isAccessibilityElement = YES; + map.accessibilityElementsHidden = NO; map.settings.consumesGesturesInView = NO; map.indoorDisplay.delegate = self; self.map = map; @@ -65,6 +65,7 @@ - (UIView *)view return map; } +RCT_REMAP_VIEW_PROPERTY(testID, accessibilityIdentifier, NSString) RCT_EXPORT_VIEW_PROPERTY(initialCamera, GMSCameraPosition) RCT_REMAP_VIEW_PROPERTY(camera, cameraProp, GMSCameraPosition) RCT_EXPORT_VIEW_PROPERTY(initialRegion, MKCoordinateRegion) diff --git a/lib/ios/AirGoogleMaps/AIRGoogleMapMarkerManager.m b/lib/ios/AirGoogleMaps/AIRGoogleMapMarkerManager.m index dd04c7542..f97a9b18a 100644 --- a/lib/ios/AirGoogleMaps/AIRGoogleMapMarkerManager.m +++ b/lib/ios/AirGoogleMaps/AIRGoogleMapMarkerManager.m @@ -25,8 +25,8 @@ - (UIView *)view // tapGestureRecognizer.cancelsTouchesInView = NO; // [marker addGestureRecognizer:tapGestureRecognizer]; marker.bridge = self.bridge; - marker.isAccessibilityElement = "YES"; - marker.accessibilityElementsHidden = "NO"; + marker.isAccessibilityElement = YES; + marker.accessibilityElementsHidden = NO; return marker; } diff --git a/lib/ios/AirMaps/AIRMapManager.m b/lib/ios/AirMaps/AIRMapManager.m index 33dbd20b2..89b931ea1 100644 --- a/lib/ios/AirMaps/AIRMapManager.m +++ b/lib/ios/AirMaps/AIRMapManager.m @@ -50,6 +50,9 @@ - (UIView *)view AIRMap *map = [AIRMap new]; map.delegate = self; + map.isAccessibilityElement = YES; + map.accessibilityElementsHidden = NO; + // MKMapView doesn't report tap events, so we attach gesture recognizers to it UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleMapTap:)]; UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleMapLongPress:)]; @@ -70,6 +73,7 @@ - (UIView *)view return map; } +RCT_REMAP_VIEW_PROPERTY(testID, accessibilityIdentifier, NSString) RCT_EXPORT_VIEW_PROPERTY(showsUserLocation, BOOL) RCT_EXPORT_VIEW_PROPERTY(userLocationAnnotationTitle, NSString) RCT_EXPORT_VIEW_PROPERTY(followsUserLocation, BOOL) diff --git a/lib/ios/AirMaps/AIRMapMarkerManager.m b/lib/ios/AirMaps/AIRMapMarkerManager.m index 7e02ef801..ddadd6104 100644 --- a/lib/ios/AirMaps/AIRMapMarkerManager.m +++ b/lib/ios/AirMaps/AIRMapMarkerManager.m @@ -27,10 +27,13 @@ - (UIView *)view AIRMapMarker *marker = [AIRMapMarker new]; [marker addTapGestureRecognizer]; marker.bridge = self.bridge; + marker.isAccessibilityElement = YES; + marker.accessibilityElementsHidden = NO; return marker; } RCT_EXPORT_VIEW_PROPERTY(identifier, NSString) +RCT_REMAP_VIEW_PROPERTY(testID, accessibilityIdentifier, NSString) //RCT_EXPORT_VIEW_PROPERTY(reuseIdentifier, NSString) RCT_EXPORT_VIEW_PROPERTY(title, NSString) RCT_REMAP_VIEW_PROPERTY(description, subtitle, NSString) From 4c2d8e3d4dcac435cd07b9b9130136e8487055e8 Mon Sep 17 00:00:00 2001 From: Maurits Dijkstra Date: Wed, 24 Apr 2019 16:00:06 +0200 Subject: [PATCH 15/27] Allow the Google Maps custom map style to be changed after the map has been initialized. (#2017) --- lib/components/MapView.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/components/MapView.js b/lib/components/MapView.js index 3123d647f..74f9e720f 100644 --- a/lib/components/MapView.js +++ b/lib/components/MapView.js @@ -533,6 +533,10 @@ class MapView extends React.Component { } componentWillUpdate(nextProps) { + if (nextProps.customMapStyle !== this.props.customMapStyle) { + this._updateStyle(nextProps); + } + const a = this.__lastRegion; const b = nextProps.region; if (!a || !b) return; @@ -549,12 +553,12 @@ class MapView extends React.Component { componentDidMount() { const { isReady } = this.state; if (isReady) { - this._updateStyle(); + this._updateStyle(this.props); } } - _updateStyle() { - const { customMapStyle } = this.props; + _updateStyle(props) { + const { customMapStyle } = props; this.map.setNativeProps({ customMapStyleString: JSON.stringify(customMapStyle) }); } @@ -565,7 +569,7 @@ class MapView extends React.Component { } else if (initialRegion) { this.map.setNativeProps({ initialRegion }); } - this._updateStyle(); + this._updateStyle(this.props); this.setState({ isReady: true }, () => { if (onMapReady) onMapReady(); }); From 666d366bd616176c5dd137c67db43910bb84e8be Mon Sep 17 00:00:00 2001 From: lantz moore Date: Wed, 24 Apr 2019 07:17:21 -0700 Subject: [PATCH 16/27] * getMapBoundaries really returns a Promise (#2828) --- index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index 2637a05d2..5747ab822 100644 --- a/index.d.ts +++ b/index.d.ts @@ -238,7 +238,7 @@ declare module "react-native-maps" { fitToSuppliedMarkers(markers: string[], options?: { edgePadding?: EdgePadding, animated?: boolean }): void; fitToCoordinates(coordinates?: LatLng[], options?: { edgePadding?: EdgePadding, animated?: boolean }): void; setMapBoundaries(northEast: LatLng, southWest: LatLng): void; - getMapBoundaries(): {northEast: LatLng; southWest: LatLng}; + getMapBoundaries(): Promise<{northEast: LatLng; southWest: LatLng}>; takeSnapshot(options?: SnapshotOptions): Promise; pointForCoordinate(coordinate: LatLng): Promise; coordinateForPoint(point: Point): Promise; From 89aeafc7b60bab8b3a9dfa4a1157300ef73ca81e Mon Sep 17 00:00:00 2001 From: Darkiman Date: Thu, 25 Apr 2019 12:07:10 +0300 Subject: [PATCH 17/27] fixed android installation instruction (#2830) * fixed android installation instruction * fixed version --- docs/installation.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index cc2139399..6d4c1d602 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -204,7 +204,7 @@ ext { targetSdkVersion = 26 buildToolsVersion = "26.0.2" supportLibVersion = "26.1.0" - googlePlayServicesVersion = "11.8.0" + googlePlayServicesVersion = "16.1.0" // or set latest version androidMapsUtilsVersion = "0.5+" } ``` @@ -272,7 +272,6 @@ import com.airbnb.android.react.maps.MapsPackage; from there you will see a button to update it (do not search within the Play Store). - ## Troubleshooting ### The map background is blank (Google Maps) @@ -295,7 +294,6 @@ For reference, you may read the relevant issue reports: ([#118](https://github.c Ensure the map component and its container have viewport dimensions. An example is below: - ```jsx import MapView, { PROVIDER_GOOGLE } from 'react-native-maps'; // remove PROVIDER_GOOGLE import if not using Google Maps ... @@ -434,3 +432,10 @@ import com.airbnb.android.react.maps.MapsPackage; ``` + +### Trouble with Google Play services + +- Make sure that your emulator has Google Play (Go to Anroid studio -> Virtual Devices -> Check that you have icon in "Play Store" column) +- Click to bottom dots icon in the emulator +- Go to Google Play Tab and click Update + From a5486a25ebb09e0877c843621a91b9b97f2fa4f1 Mon Sep 17 00:00:00 2001 From: Alexander Date: Thu, 2 May 2019 11:42:42 +0300 Subject: [PATCH 18/27] Update installation.md (#2838) Workaround for Android build issue --- docs/installation.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/installation.md b/docs/installation.md index 6d4c1d602..f0cc9377f 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -439,3 +439,15 @@ import com.airbnb.android.react.maps.MapsPackage; - Click to bottom dots icon in the emulator - Go to Google Play Tab and click Update + +### Android build error: "Program type already present" + +If you **don't** use project-wide properties as per instructions above (not making changes to global android/build.gradle) and encounter at build time "Program type already present" error - add those lines to your android/app/build.gradle in the dependencies section: + + dependencies { + ... + implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}" + implementation "com.android.support:design:${rootProject.ext.supportLibVersion}" + implementation "com.android.support:support-v4:${rootProject.ext.supportLibVersion}" + } + From 1c2b8f894f1f1f008ce7628ee54f0afbab21bd85 Mon Sep 17 00:00:00 2001 From: Jason Li Date: Wed, 8 May 2019 23:27:25 -0700 Subject: [PATCH 19/27] Add compass location offsets for iOS maps (#2397) * Added compass offset for iOS * Fixed import type * Updated doc --- docs/mapview.md | 1 + index.d.ts | 1 + lib/components/MapView.js | 8 ++++++++ lib/ios/AirMaps/AIRMap.h | 1 + lib/ios/AirMaps/AIRMap.m | 10 ++++++++++ lib/ios/AirMaps/AIRMapManager.m | 1 + 6 files changed, 22 insertions(+) diff --git a/docs/mapview.md b/docs/mapview.md index acb8bcf8d..00d11c259 100644 --- a/docs/mapview.md +++ b/docs/mapview.md @@ -41,6 +41,7 @@ | `moveOnMarkerPress` | `Boolean` | `true` | `Android only` If `false` the map won't move when a marker is pressed. | `legalLabelInsets` | `EdgeInsets` | | If set, changes the position of the "Legal" label link from the OS default. **Note:** iOS only. | `kmlSrc` | `string` | | The URL from KML file. **Note:** Google Maps and Markers only (either Android or iOS with `PROVIDER_GOOGLE`). +| `compassOffset` | `Point` | | If set, changes the position of the compass. **Note:** iOS Maps only. ## Events diff --git a/index.d.ts b/index.d.ts index 5747ab822..d83be2179 100644 --- a/index.d.ts +++ b/index.d.ts @@ -202,6 +202,7 @@ declare module "react-native-maps" { maxDelta?: number; minDelta?: number; legalLabelInsets?: EdgeInsets; + compassOffset?: { x: number; y: number }; onMapReady?: () => void; onKmlReady?: (values: KmlMapEvent) => void; diff --git a/lib/components/MapView.js b/lib/components/MapView.js index 74f9e720f..bf33db624 100644 --- a/lib/components/MapView.js +++ b/lib/components/MapView.js @@ -2,6 +2,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { EdgeInsetsPropType, + PointPropType, Platform, Animated as RNAnimated, requireNativeComponent, @@ -503,6 +504,13 @@ const propTypes = { */ kmlSrc: PropTypes.string, + /** + * Offset Point x y for compass location. + * + * @platform ios + */ + compassOffset: PointPropType, + /** * Callback that is called when a level is activated on a indoor building. */ diff --git a/lib/ios/AirMaps/AIRMap.h b/lib/ios/AirMaps/AIRMap.h index 26ce296f8..e70d8f14c 100644 --- a/lib/ios/AirMaps/AIRMap.h +++ b/lib/ios/AirMaps/AIRMap.h @@ -44,6 +44,7 @@ extern const NSInteger AIRMapMaxZoomLevel; @property (nonatomic, assign) MKMapCamera *initialCamera; @property (nonatomic, assign) CGFloat minZoomLevel; @property (nonatomic, assign) CGFloat maxZoomLevel; +@property (nonatomic, assign) CGPoint compassOffset; @property (nonatomic, assign) CLLocationCoordinate2D pendingCenter; @property (nonatomic, assign) MKCoordinateSpan pendingSpan; diff --git a/lib/ios/AirMaps/AIRMap.m b/lib/ios/AirMaps/AIRMap.m index dc3bc0978..6db04960c 100644 --- a/lib/ios/AirMaps/AIRMap.m +++ b/lib/ios/AirMaps/AIRMap.m @@ -87,6 +87,7 @@ - (instancetype)init self.minZoomLevel = 0; self.maxZoomLevel = AIRMapMaxZoomLevel; + self.compassOffset = CGPointMake(0, 0); } return self; } @@ -636,6 +637,15 @@ - (UIImageView *)cacheImageView { - (void)layoutSubviews { [super layoutSubviews]; [self cacheViewIfNeeded]; + NSUInteger index = [[self subviews] indexOfObjectPassingTest:^BOOL(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + NSString *str = NSStringFromClass([obj class]); + return [str containsString:@"MKCompassView"]; + }]; + if (index != NSNotFound) { + UIView* compassButton; + compassButton = [self.subviews objectAtIndex:index]; + compassButton.frame = CGRectMake(compassButton.frame.origin.x + _compassOffset.x, compassButton.frame.origin.y + _compassOffset.y, compassButton.frame.size.width, compassButton.frame.size.height); + } } @end diff --git a/lib/ios/AirMaps/AIRMapManager.m b/lib/ios/AirMaps/AIRMapManager.m index 89b931ea1..63c93b577 100644 --- a/lib/ios/AirMaps/AIRMapManager.m +++ b/lib/ios/AirMaps/AIRMapManager.m @@ -94,6 +94,7 @@ - (UIView *)view RCT_EXPORT_VIEW_PROPERTY(handlePanDrag, BOOL) RCT_EXPORT_VIEW_PROPERTY(maxDelta, CGFloat) RCT_EXPORT_VIEW_PROPERTY(minDelta, CGFloat) +RCT_EXPORT_VIEW_PROPERTY(compassOffset, CGPoint) RCT_EXPORT_VIEW_PROPERTY(legalLabelInsets, UIEdgeInsets) RCT_EXPORT_VIEW_PROPERTY(mapType, MKMapType) RCT_EXPORT_VIEW_PROPERTY(onMapReady, RCTBubblingEventBlock) From 5735436b8a60b77cbb3dba1cbd300d48414e0ee2 Mon Sep 17 00:00:00 2001 From: Taym Haddadi Date: Wed, 15 May 2019 14:08:16 +0200 Subject: [PATCH 20/27] Remove componentWillUpdate (#2849) * Use getSnapshotBeforeUpdate and componentDidUpdate instead of componentWillUpdate * Try to fix travis linter * Fix travis linter --- lib/components/MapView.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/components/MapView.js b/lib/components/MapView.js index bf33db624..159a258c3 100644 --- a/lib/components/MapView.js +++ b/lib/components/MapView.js @@ -540,13 +540,16 @@ class MapView extends React.Component { return { provider: this.props.provider }; } - componentWillUpdate(nextProps) { - if (nextProps.customMapStyle !== this.props.customMapStyle) { - this._updateStyle(nextProps); + getSnapshotBeforeUpdate(prevProps) { + if (this.props.customMapStyle !== prevProps.customMapStyle) { + this._updateStyle(this.props); } + return this.props.region; + } + componentDidUpdate(prevProps, prevState, region) { const a = this.__lastRegion; - const b = nextProps.region; + const b = region; if (!a || !b) return; if ( a.latitude !== b.latitude || From dbf746d66ca1b42f2beb790bfbf4c0e3a74f3279 Mon Sep 17 00:00:00 2001 From: Dan Tamas Date: Wed, 15 May 2019 15:08:19 +0200 Subject: [PATCH 21/27] Unify eslint with react-community standard (#2862) * move eslitnt rules to @react-native-community/eslint-config * gitignore VSCode workspace settings * update eslint related packages and remove airbnb ones * add .prettierrc config * automated fix by eslint and prettier * remove custom eslint rules * remove never reported elint exceptions * remove unlimited eslint-disable exceptions * fix formating * fix inline styles * fix react to 16.8.3 and remove babel-preset-react-native --- .eslintrc | 14 +- .gitignore | 3 + .prettierrc | 4 + example/App.js | 117 +- example/examples/AnimatedMarkers.js | 14 +- example/examples/AnimatedNavigation.js | 19 +- example/examples/AnimatedPriceMarker.js | 11 +- example/examples/AnimatedViews.js | 204 +- example/examples/BugMarkerWontUpdate.js | 8 +- example/examples/CachedMap.js | 18 +- example/examples/Callouts.js | 58 +- example/examples/CameraControl.js | 21 +- example/examples/CustomCallout.js | 9 +- example/examples/CustomOverlay.js | 8 +- .../examples/CustomOverlayXMarksTheSpot.js | 4 +- example/examples/CustomTiles.js | 19 +- example/examples/CustomTilesLocal.js | 12 +- example/examples/DefaultMarkers.js | 6 +- example/examples/DisplayLatLng.js | 15 +- example/examples/DraggableMarkers.js | 26 +- example/examples/EventListener.js | 95 +- example/examples/FitToCoordinates.js | 14 +- example/examples/FitToSuppliedMarkers.js | 67 +- example/examples/GradientPolylines.js | 5 +- example/examples/ImageOverlayWithAssets.js | 7 +- example/examples/ImageOverlayWithURL.js | 17 +- example/examples/IndoorMap.js | 23 +- example/examples/LegalLabel.js | 22 +- example/examples/LiteMapView.js | 10 +- example/examples/LoadingMap.js | 7 +- example/examples/MapKml.js | 10 +- example/examples/MapStyle.js | 9 +- example/examples/MarkerTypes.js | 7 +- example/examples/MassiveCustomMarkers.js | 4 +- example/examples/MyLocationMapMarker.js | 59 +- example/examples/OnPoiClick.js | 15 +- example/examples/Overlays.js | 20 +- example/examples/PanController.js | 208 +- example/examples/PolygonCreator.js | 24 +- example/examples/PolylineCreator.js | 9 +- example/examples/PriceMarker.js | 6 +- example/examples/SetNativePropsOverlays.js | 43 +- example/examples/StaticMap.js | 8 +- example/examples/TakeSnapshot.js | 36 +- example/examples/TestIdMarkers.js | 17 +- example/examples/ViewsAsMarkers.js | 5 +- example/examples/WMSTiles.js | 13 +- example/examples/ZIndexMarkers.js | 25 +- index.d.ts | 945 +- index.js | 11 +- lib/components/AnimatedRegion.js | 126 +- lib/components/MapCallout.js | 13 +- lib/components/MapCalloutSubview.js | 22 +- lib/components/MapCircle.js | 25 +- lib/components/MapLocalTile.js | 11 +- lib/components/MapMarker.js | 5 +- lib/components/MapOverlay.js | 14 +- lib/components/MapPolygon.js | 59 +- lib/components/MapPolyline.js | 45 +- lib/components/MapUrlTile.js | 11 +- lib/components/MapView.js | 147 +- lib/components/MapWMSTile.js | 12 +- lib/components/decorateMapComponent.js | 69 +- package-lock.json | 12877 ++++++++++++++++ package.json | 19 +- scripts/update-version.js | 24 +- 66 files changed, 14388 insertions(+), 1422 deletions(-) create mode 100644 .prettierrc create mode 100644 package-lock.json diff --git a/.eslintrc b/.eslintrc index 7b768ff92..150be1bd8 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,17 +1,7 @@ { "parser": "babel-eslint", - "extends": "airbnb", + "extends": "@react-native-community", "plugins": [ "prefer-object-spread" - ], - "rules": { - "prefer-object-spread/prefer-object-spread": 2, - "react/jsx-filename-extension": 0, - "react/prefer-stateless-function": 0, - "react/sort-comp": 0, - "no-use-before-define": 0, - "no-underscore-dangle": 0, - "import/no-extraneous-dependencies": 0, - "import/no-unresolved": [2, { "ignore": ["^react-native-maps"] }] - } + ] } diff --git a/.gitignore b/.gitignore index 883c1fbcd..0766de3c6 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,6 @@ yarn-error.log # Bundle artifact *.jsbundle + +# VSCode workspace settings +.vscode diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..e409ca61e --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "trailingComma": "es5", + "singleQuote": true +} diff --git a/example/App.js b/example/App.js index bec38b98a..11fc0be75 100644 --- a/example/App.js +++ b/example/App.js @@ -91,7 +91,7 @@ export default class App extends React.Component { style={styles.back} onPress={() => this.setState({ Component: null })} > - + ); } @@ -101,8 +101,8 @@ export default class App extends React.Component { Use GoogleMaps? this.setState({ useGoogleMaps: value })} - style={{ marginBottom: 10 }} + onValueChange={value => this.setState({ useGoogleMaps: value })} + style={styles.googleSwitch} value={this.state.useGoogleMaps} /> @@ -110,16 +110,17 @@ export default class App extends React.Component { } renderExamples(examples) { - const { - Component, - useGoogleMaps, - } = this.state; + const { Component, useGoogleMaps } = this.state; return ( - {Component && } + {Component && ( + + )} {Component && this.renderBackButton()} - {!Component && + {!Component && ( { {IOS && this.renderGoogleSwitch()} {examples.map(example => this.renderExample(example))} - } + )} ); } render() { - return this.renderExamples([ - // [, , , ] - [StaticMap, 'StaticMap', true], - [DisplayLatLng, 'Tracking Position', true, '(incomplete)'], - [ViewsAsMarkers, 'Arbitrary Views as Markers', true], - [EventListener, 'Events', true, '(incomplete)'], - [MarkerTypes, 'Image Based Markers', true], - [DraggableMarkers, 'Draggable Markers', true], - [PolygonCreator, 'Polygon Creator', true], - [PolylineCreator, 'Polyline Creator', true], - [GradientPolylines, 'Gradient Polylines', true], - [AnimatedViews, 'Animating with MapViews'], - [AnimatedMarkers, 'Animated Marker Position'], - [Callouts, 'Custom Callouts', true], - [Overlays, 'Circles, Polygons, and Polylines', true], - [DefaultMarkers, 'Default Markers', true], - [CustomMarkers, 'Custom Markers', true], - [TakeSnapshot, 'Take Snapshot', true, '(incomplete)'], - [CachedMap, 'Cached Map'], - [LoadingMap, 'Map with loading'], - [MapBoundaries, 'Get visible map boundaries', true], - [FitToSuppliedMarkers, 'Focus Map On Markers', true], - [FitToCoordinates, 'Fit Map To Coordinates', true], - [LiteMapView, 'Android Lite MapView'], - [CustomTiles, 'Custom Tiles', true], - [WMSTiles, 'WMS Tiles', true], - [ZIndexMarkers, 'Position Markers with Z-index', true], - [MapStyle, 'Customize the style of the map', true], - [LegalLabel, 'Reposition the legal label', true], - [SetNativePropsOverlays, 'Update native props', true], - [CustomOverlay, 'Custom Overlay Component', true], - [TestIdMarkers, 'Test ID for Automation', true], - [MapKml, 'Load Map with KML', true], - [BugMarkerWontUpdate, 'BUG: Marker Won\'t Update (Android)', true], - [ImageOverlayWithAssets, 'Image Overlay Component with Assets', true], - [ImageOverlayWithURL, 'Image Overlay Component with URL', true], - [AnimatedNavigation, 'Animated Map Navigation', true], - [OnPoiClick, 'On Poi Click', true], - [IndoorMap, 'Indoor Map', true], - [CameraControl, 'CameraControl', true], - [MassiveCustomMarkers, 'MassiveCustomMarkers', true], - ] - // Filter out examples that are not yet supported for Google Maps on iOS. - .filter(example => ANDROID || (IOS && (example[2] || !this.state.useGoogleMaps))) - .map(makeExampleMapper(IOS && this.state.useGoogleMaps)) + return this.renderExamples( + [ + // [, , , ] + [StaticMap, 'StaticMap', true], + [DisplayLatLng, 'Tracking Position', true, '(incomplete)'], + [ViewsAsMarkers, 'Arbitrary Views as Markers', true], + [EventListener, 'Events', true, '(incomplete)'], + [MarkerTypes, 'Image Based Markers', true], + [DraggableMarkers, 'Draggable Markers', true], + [PolygonCreator, 'Polygon Creator', true], + [PolylineCreator, 'Polyline Creator', true], + [GradientPolylines, 'Gradient Polylines', true], + [AnimatedViews, 'Animating with MapViews'], + [AnimatedMarkers, 'Animated Marker Position'], + [Callouts, 'Custom Callouts', true], + [Overlays, 'Circles, Polygons, and Polylines', true], + [DefaultMarkers, 'Default Markers', true], + [CustomMarkers, 'Custom Markers', true], + [TakeSnapshot, 'Take Snapshot', true, '(incomplete)'], + [CachedMap, 'Cached Map'], + [LoadingMap, 'Map with loading'], + [MapBoundaries, 'Get visible map boundaries', true], + [FitToSuppliedMarkers, 'Focus Map On Markers', true], + [FitToCoordinates, 'Fit Map To Coordinates', true], + [LiteMapView, 'Android Lite MapView'], + [CustomTiles, 'Custom Tiles', true], + [WMSTiles, 'WMS Tiles', true], + [ZIndexMarkers, 'Position Markers with Z-index', true], + [MapStyle, 'Customize the style of the map', true], + [LegalLabel, 'Reposition the legal label', true], + [SetNativePropsOverlays, 'Update native props', true], + [CustomOverlay, 'Custom Overlay Component', true], + [TestIdMarkers, 'Test ID for Automation', true], + [MapKml, 'Load Map with KML', true], + [BugMarkerWontUpdate, "BUG: Marker Won't Update (Android)", true], + [ImageOverlayWithAssets, 'Image Overlay Component with Assets', true], + [ImageOverlayWithURL, 'Image Overlay Component with URL', true], + [AnimatedNavigation, 'Animated Map Navigation', true], + [OnPoiClick, 'On Poi Click', true], + [IndoorMap, 'Indoor Map', true], + [CameraControl, 'CameraControl', true], + [MassiveCustomMarkers, 'MassiveCustomMarkers', true], + ] + // Filter out examples that are not yet supported for Google Maps on iOS. + .filter( + example => + ANDROID || (IOS && (example[2] || !this.state.useGoogleMaps)) + ) + .map(makeExampleMapper(IOS && this.state.useGoogleMaps)) ); } } @@ -212,4 +217,6 @@ const styles = StyleSheet.create({ alignItems: 'center', justifyContent: 'center', }, + backButton: { fontWeight: 'bold', fontSize: 30 }, + googleSwitch: { marginBottom: 10 }, }); diff --git a/example/examples/AnimatedMarkers.js b/example/examples/AnimatedMarkers.js index 98400cf80..957788dc7 100644 --- a/example/examples/AnimatedMarkers.js +++ b/example/examples/AnimatedMarkers.js @@ -8,7 +8,11 @@ import { Platform, } from 'react-native'; -import MapView, { ProviderPropType, Marker, AnimatedRegion } from 'react-native-maps'; +import MapView, { + ProviderPropType, + Marker, + AnimatedRegion, +} from 'react-native-maps'; const screen = Dimensions.get('window'); @@ -33,8 +37,8 @@ class AnimatedMarkers extends React.Component { animate() { const { coordinate } = this.state; const newCoordinate = { - latitude: LATITUDE + ((Math.random() - 0.5) * (LATITUDE_DELTA / 2)), - longitude: LONGITUDE + ((Math.random() - 0.5) * (LONGITUDE_DELTA / 2)), + latitude: LATITUDE + (Math.random() - 0.5) * (LATITUDE_DELTA / 2), + longitude: LONGITUDE + (Math.random() - 0.5) * (LONGITUDE_DELTA / 2), }; if (Platform.OS === 'android') { @@ -60,7 +64,9 @@ class AnimatedMarkers extends React.Component { }} > { this.marker = marker; }} + ref={marker => { + this.marker = marker; + }} coordinate={this.state.coordinate} /> diff --git a/example/examples/AnimatedNavigation.js b/example/examples/AnimatedNavigation.js index 4b853539a..69ce11341 100644 --- a/example/examples/AnimatedNavigation.js +++ b/example/examples/AnimatedNavigation.js @@ -1,17 +1,11 @@ import React, { Component } from 'react'; -import { - View, - StyleSheet, - TouchableOpacity, - Text, -} from 'react-native'; +import { View, StyleSheet, TouchableOpacity, Text } from 'react-native'; import MapView from 'react-native-maps'; import carImage from './assets/car.png'; export default class NavigationMap extends Component { - constructor(props) { super(props); this.state = { @@ -29,12 +23,17 @@ export default class NavigationMap extends Component { changePosition(latOffset, lonOffset) { const latitude = this.state.curPos.latitude + latOffset; const longitude = this.state.curPos.longitude + lonOffset; - this.setState({ prevPos: this.state.curPos, curPos: { latitude, longitude } }); + this.setState({ + prevPos: this.state.curPos, + curPos: { latitude, longitude }, + }); this.updateMap(); } getRotation(prevPos, curPos) { - if (!prevPos) return 0; + if (!prevPos) { + return 0; + } const xDiff = curPos.latitude - prevPos.latitude; const yDiff = curPos.longitude - prevPos.longitude; return (Math.atan2(yDiff, xDiff) * 180.0) / Math.PI; @@ -50,7 +49,7 @@ export default class NavigationMap extends Component { return ( (this.map = el)} + ref={el => (this.map = el)} style={styles.flex} minZoomLevel={15} initialRegion={{ diff --git a/example/examples/AnimatedPriceMarker.js b/example/examples/AnimatedPriceMarker.js index edcda94ef..f486e2d8b 100644 --- a/example/examples/AnimatedPriceMarker.js +++ b/example/examples/AnimatedPriceMarker.js @@ -1,11 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { - StyleSheet, - Text, - Animated, -} from 'react-native'; +import { StyleSheet, Text, Animated } from 'react-native'; class AnimatedPriceMarker extends React.Component { render() { @@ -38,9 +34,7 @@ class AnimatedPriceMarker extends React.Component { - + ); } @@ -98,7 +92,6 @@ const styles = StyleSheet.create({ }, selectedArrow: { borderTopColor: '#4da2ab', - }, selectedArrowBorder: { borderTopColor: '#007a87', diff --git a/example/examples/AnimatedViews.js b/example/examples/AnimatedViews.js index a16b2f192..dedd346e2 100644 --- a/example/examples/AnimatedViews.js +++ b/example/examples/AnimatedViews.js @@ -1,10 +1,5 @@ import React from 'react'; -import { - StyleSheet, - View, - Dimensions, - Animated, -} from 'react-native'; +import { StyleSheet, View, Dimensions, Animated } from 'react-native'; import { ProviderPropType, @@ -25,7 +20,7 @@ const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; const ITEM_SPACING = 10; const ITEM_PREVIEW = 10; -const ITEM_WIDTH = screen.width - (2 * ITEM_SPACING) - (2 * ITEM_PREVIEW); +const ITEM_WIDTH = screen.width - 2 * ITEM_SPACING - 2 * ITEM_PREVIEW; const SNAP_WIDTH = ITEM_WIDTH + ITEM_SPACING; const ITEM_PREVIEW_HEIGHT = 150; const SCALE_END = screen.width / ITEM_WIDTH; @@ -34,8 +29,8 @@ const BREAKPOINT2 = 350; const ONE = new Animated.Value(1); function getMarkerState(panX, panY, scrollY, i) { - const xLeft = (-SNAP_WIDTH * i) + (SNAP_WIDTH / 2); - const xRight = (-SNAP_WIDTH * i) - (SNAP_WIDTH / 2); + const xLeft = -SNAP_WIDTH * i + SNAP_WIDTH / 2; + const xRight = -SNAP_WIDTH * i - SNAP_WIDTH / 2; const xPos = -SNAP_WIDTH * i; const isIndex = panX.interpolate({ @@ -66,17 +61,26 @@ function getMarkerState(panX, panY, scrollY, i) { const translateX = panX; - const anim = Animated.multiply(isIndex, scrollY.interpolate({ - inputRange: [0, BREAKPOINT1], - outputRange: [0, 1], - extrapolate: 'clamp', - })); - - const scale = Animated.add(ONE, Animated.multiply(isIndex, scrollY.interpolate({ - inputRange: [BREAKPOINT1, BREAKPOINT2], - outputRange: [0, SCALE_END - 1], - extrapolate: 'clamp', - }))); + const anim = Animated.multiply( + isIndex, + scrollY.interpolate({ + inputRange: [0, BREAKPOINT1], + outputRange: [0, 1], + extrapolate: 'clamp', + }) + ); + + const scale = Animated.add( + ONE, + Animated.multiply( + isIndex, + scrollY.interpolate({ + inputRange: [BREAKPOINT1, BREAKPOINT2], + outputRange: [0, SCALE_END - 1], + extrapolate: 'clamp', + }) + ) + ); // [0 => 1] let opacity = scrollY.interpolate({ @@ -89,7 +93,6 @@ function getMarkerState(panX, panY, scrollY, i) { // if i !== index: [0 => 1] opacity = Animated.multiply(isNotIndex, opacity); - // if i === index: [1 => 1] // if i !== index: [1 => 0] opacity = opacity.interpolate({ @@ -183,7 +186,8 @@ class AnimatedViews extends React.Component { ]; const animations = markers.map((m, i) => - getMarkerState(panX, panY, scrollY, i)); + getMarkerState(panX, panY, scrollY, i) + ); this.state = { panX, @@ -212,20 +216,22 @@ class AnimatedViews extends React.Component { panY.addListener(this.onPanYChange); region.stopAnimation(); - region.timing({ - latitude: scrollX.interpolate({ - inputRange: markers.map((m, i) => i * SNAP_WIDTH), - outputRange: markers.map(m => m.coordinate.latitude), - }), - longitude: scrollX.interpolate({ - inputRange: markers.map((m, i) => i * SNAP_WIDTH), - outputRange: markers.map(m => m.coordinate.longitude), - }), - duration: 0, - }).start(); + region + .timing({ + latitude: scrollX.interpolate({ + inputRange: markers.map((m, i) => i * SNAP_WIDTH), + outputRange: markers.map(m => m.coordinate.latitude), + }), + longitude: scrollX.interpolate({ + inputRange: markers.map((m, i) => i * SNAP_WIDTH), + outputRange: markers.map(m => m.coordinate.longitude), + }), + duration: 0, + }) + .start(); } - onStartShouldSetPanResponder = (e) => { + onStartShouldSetPanResponder = e => { // we only want to move the view if they are starting the gesture on top // of the view, so this calculates that and returns true if so. If we return // false, the gesture should get passed to the map view appropriately. @@ -235,70 +241,81 @@ class AnimatedViews extends React.Component { const topOfTap = screen.height - pageY; return topOfTap < topOfMainWindow; - } + }; - onMoveShouldSetPanResponder = (e) => { + onMoveShouldSetPanResponder = e => { const { panY } = this.state; const { pageY } = e.nativeEvent; const topOfMainWindow = ITEM_PREVIEW_HEIGHT + panY.__getValue(); const topOfTap = screen.height - pageY; return topOfTap < topOfMainWindow; - } + }; onPanXChange = ({ value }) => { const { index } = this.state; - const newIndex = Math.floor(((-1 * value) + (SNAP_WIDTH / 2)) / SNAP_WIDTH); + const newIndex = Math.floor((-1 * value + SNAP_WIDTH / 2) / SNAP_WIDTH); if (index !== newIndex) { this.setState({ index: newIndex }); } - } + }; onPanYChange = ({ value }) => { - const { canMoveHorizontal, region, scrollY, scrollX, markers, index } = this.state; + const { + canMoveHorizontal, + region, + scrollY, + scrollX, + markers, + index, + } = this.state; const shouldBeMovable = Math.abs(value) < 2; if (shouldBeMovable !== canMoveHorizontal) { this.setState({ canMoveHorizontal: shouldBeMovable }); if (!shouldBeMovable) { const { coordinate } = markers[index]; region.stopAnimation(); - region.timing({ - latitude: scrollY.interpolate({ - inputRange: [0, BREAKPOINT1], - outputRange: [ - coordinate.latitude, - coordinate.latitude - (LATITUDE_DELTA * 0.5 * 0.375), - ], - extrapolate: 'clamp', - }), - latitudeDelta: scrollY.interpolate({ - inputRange: [0, BREAKPOINT1], - outputRange: [LATITUDE_DELTA, LATITUDE_DELTA * 0.5], - extrapolate: 'clamp', - }), - longitudeDelta: scrollY.interpolate({ - inputRange: [0, BREAKPOINT1], - outputRange: [LONGITUDE_DELTA, LONGITUDE_DELTA * 0.5], - extrapolate: 'clamp', - }), - duration: 0, - }).start(); + region + .timing({ + latitude: scrollY.interpolate({ + inputRange: [0, BREAKPOINT1], + outputRange: [ + coordinate.latitude, + coordinate.latitude - LATITUDE_DELTA * 0.5 * 0.375, + ], + extrapolate: 'clamp', + }), + latitudeDelta: scrollY.interpolate({ + inputRange: [0, BREAKPOINT1], + outputRange: [LATITUDE_DELTA, LATITUDE_DELTA * 0.5], + extrapolate: 'clamp', + }), + longitudeDelta: scrollY.interpolate({ + inputRange: [0, BREAKPOINT1], + outputRange: [LONGITUDE_DELTA, LONGITUDE_DELTA * 0.5], + extrapolate: 'clamp', + }), + duration: 0, + }) + .start(); } else { region.stopAnimation(); - region.timing({ - latitude: scrollX.interpolate({ - inputRange: markers.map((m, i) => i * SNAP_WIDTH), - outputRange: markers.map(m => m.coordinate.latitude), - }), - longitude: scrollX.interpolate({ - inputRange: markers.map((m, i) => i * SNAP_WIDTH), - outputRange: markers.map(m => m.coordinate.longitude), - }), - duration: 0, - }).start(); + region + .timing({ + latitude: scrollX.interpolate({ + inputRange: markers.map((m, i) => i * SNAP_WIDTH), + outputRange: markers.map(m => m.coordinate.latitude), + }), + longitude: scrollX.interpolate({ + inputRange: markers.map((m, i) => i * SNAP_WIDTH), + outputRange: markers.map(m => m.coordinate.longitude), + }), + duration: 0, + }) + .start(); } } - } + }; onRegionChange(/* region */) { // this.state.region.setValue(region); @@ -336,23 +353,14 @@ class AnimatedViews extends React.Component { onRegionChange={this.onRegionChange} > {markers.map((marker, i) => { - const { - selected, - markerOpacity, - markerScale, - } = animations[i]; + const { selected, markerOpacity, markerScale } = animations[i]; return ( - + {markers.map((marker, i) => { - const { - translateY, - translateX, - scale, - opacity, - } = animations[i]; + const { translateY, translateX, scale, opacity } = animations[i]; return ( ); })} @@ -402,7 +404,7 @@ const styles = StyleSheet.create({ itemContainer: { backgroundColor: 'transparent', flexDirection: 'row', - paddingHorizontal: (ITEM_SPACING / 2) + ITEM_PREVIEW, + paddingHorizontal: ITEM_SPACING / 2 + ITEM_PREVIEW, position: 'absolute', // top: screen.height - ITEM_PREVIEW_HEIGHT - 64, paddingTop: screen.height - ITEM_PREVIEW_HEIGHT - 64, @@ -414,7 +416,7 @@ const styles = StyleSheet.create({ }, item: { width: ITEM_WIDTH, - height: screen.height + (2 * ITEM_PREVIEW_HEIGHT), + height: screen.height + 2 * ITEM_PREVIEW_HEIGHT, backgroundColor: 'red', marginHorizontal: ITEM_SPACING / 2, overflow: 'hidden', diff --git a/example/examples/BugMarkerWontUpdate.js b/example/examples/BugMarkerWontUpdate.js index 8b2f697e2..72d016824 100644 --- a/example/examples/BugMarkerWontUpdate.js +++ b/example/examples/BugMarkerWontUpdate.js @@ -68,7 +68,7 @@ class BugMarkerWontUpdate extends React.Component { onPress={() => this.toggleHack()} style={[styles.bubble, styles.button, styles.hackButton]} > - + {this.state.enableHack ? 'Disable Hack' : 'Enable Hack'} @@ -78,13 +78,13 @@ class BugMarkerWontUpdate extends React.Component { onPress={() => this.decrement()} style={[styles.bubble, styles.button]} > - - + - this.increment()} style={[styles.bubble, styles.button]} > - + + + @@ -129,6 +129,8 @@ const styles = StyleSheet.create({ marginVertical: 20, backgroundColor: 'transparent', }, + toggleHack: { fontSize: 12, fontWeight: 'bold' }, + ammountButton: { fontSize: 20, fontWeight: 'bold' }, }); export default BugMarkerWontUpdate; diff --git a/example/examples/CachedMap.js b/example/examples/CachedMap.js index 2be98d2e4..f17c10501 100644 --- a/example/examples/CachedMap.js +++ b/example/examples/CachedMap.js @@ -18,7 +18,9 @@ class CachedMap extends React.Component { constructor(props) { super(props); - const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 }); + const ds = new ListView.DataSource({ + rowHasChanged: (r1, r2) => r1 !== r2, + }); this.state = { dataSource: ds.cloneWithRows(COUNTRIES), cache: true, @@ -38,7 +40,7 @@ class CachedMap extends React.Component { render() { const { width } = Dimensions.get('window'); - const mapSize = width - (HORIZONTAL_PADDING * 2); + const mapSize = width - HORIZONTAL_PADDING * 2; return ( @@ -46,15 +48,15 @@ class CachedMap extends React.Component { onPress={() => this.toggleCache()} style={[styles.bubble, styles.button]} > - {this.state.cache ? 'Cached' : 'Not cached'} + + {this.state.cache ? 'Cached' : 'Not cached'} + - + renderRow={region => ( + {region.name} - } + )} /> ); diff --git a/example/examples/Callouts.js b/example/examples/Callouts.js index 8e5a450f4..5aacc9426 100644 --- a/example/examples/Callouts.js +++ b/example/examples/Callouts.js @@ -7,7 +7,12 @@ import { TouchableOpacity, Alert, } from 'react-native'; -import MapView, { Marker, Callout, CalloutSubview, ProviderPropType } from 'react-native-maps'; +import MapView, { + Marker, + Callout, + CalloutSubview, + ProviderPropType, +} from 'react-native-maps'; import CustomCallout from './CustomCallout'; const { width, height } = Dimensions.get('window'); @@ -52,7 +57,7 @@ class Callouts extends React.Component { { coordinate: { latitude: LATITUDE, - longitude: LONGITUDE - (SPACE / 2), + longitude: LONGITUDE - SPACE / 2, }, }, ], @@ -78,14 +83,14 @@ class Callouts extends React.Component { zoomTapEnabled={false} > { this.marker1 = ref; }} + ref={ref => { + this.marker1 = ref; + }} coordinate={markers[0].coordinate} title="This is a native view" - description="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation" // eslint-disable-line max-len + description="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation" /> - + This is a plain view @@ -96,27 +101,36 @@ class Callouts extends React.Component { coordinate={markers[2].coordinate} calloutOffset={{ x: -8, y: 28 }} calloutAnchor={{ x: 0.5, y: 0.4 }} - ref={ref => { this.marker2 = ref; }} + ref={ref => { + this.marker2 = ref; + }} > { - if (e.nativeEvent.action === 'marker-inside-overlay-press' || - e.nativeEvent.action === 'callout-inside-press') { + tooltip + onPress={e => { + if ( + e.nativeEvent.action === 'marker-inside-overlay-press' || + e.nativeEvent.action === 'callout-inside-press' + ) { return; } Alert.alert('callout pressed'); - }} style={styles.customView} + }} + style={styles.customView} > - {`This is a custom callout bubble view ${this.state.cnt}`} + {`This is a custom callout bubble view ${ + this.state.cnt + }`} { this.setState({ cnt: this.state.cnt + 1 }, () => { this.marker2.redrawCallout(); }); - }} style={[styles.calloutButton]} + }} + style={[styles.calloutButton]} > Click me @@ -124,10 +138,12 @@ class Callouts extends React.Component { { this.marker4 = ref; }} + ref={ref => { + this.marker4 = ref; + }} coordinate={markers[3].coordinate} title="You can also open this callout" - description="by pressing on transparent area of custom callout" // eslint-disable-line max-len + description="by pressing on transparent area of custom callout" /> @@ -136,10 +152,16 @@ class Callouts extends React.Component { - this.show()} style={[styles.bubble, styles.button]}> + this.show()} + style={[styles.bubble, styles.button]} + > Show - this.hide()} style={[styles.bubble, styles.button]}> + this.hide()} + style={[styles.bubble, styles.button]} + > Hide diff --git a/example/examples/CameraControl.js b/example/examples/CameraControl.js index dc746bc93..3a221e009 100644 --- a/example/examples/CameraControl.js +++ b/example/examples/CameraControl.js @@ -1,29 +1,17 @@ import React from 'react'; -import { - StyleSheet, - View, - TouchableOpacity, - Text, - Alert, -} from 'react-native'; +import { StyleSheet, View, TouchableOpacity, Text, Alert } from 'react-native'; import MapView, { ProviderPropType } from 'react-native-maps'; const LATITUDE = 37.78825; const LONGITUDE = -122.4324; - class CameraControl extends React.Component { async getCamera() { const camera = await this.map.getCamera(); - Alert.alert( - 'Current camera', - JSON.stringify(camera), - [ - { text: 'OK' }, - ], - { cancelable: true } - ); + Alert.alert('Current camera', JSON.stringify(camera), [{ text: 'OK' }], { + cancelable: true, + }); } async setCamera() { @@ -94,7 +82,6 @@ CameraControl.propTypes = { provider: ProviderPropType, }; - const styles = StyleSheet.create({ container: { ...StyleSheet.absoluteFillObject, diff --git a/example/examples/CustomCallout.js b/example/examples/CustomCallout.js index 844252cc2..e59e52db3 100644 --- a/example/examples/CustomCallout.js +++ b/example/examples/CustomCallout.js @@ -1,10 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { - StyleSheet, - View, -} from 'react-native'; +import { StyleSheet, View } from 'react-native'; const propTypes = { children: PropTypes.node.isRequired, @@ -16,9 +13,7 @@ class CustomCallout extends React.Component { return ( - - {this.props.children} - + {this.props.children} diff --git a/example/examples/CustomOverlay.js b/example/examples/CustomOverlay.js index 6e5ec5d15..f71a7b875 100644 --- a/example/examples/CustomOverlay.js +++ b/example/examples/CustomOverlay.js @@ -1,9 +1,5 @@ import React from 'react'; -import { - StyleSheet, - View, - Dimensions, -} from 'react-native'; +import { StyleSheet, View, Dimensions } from 'react-native'; import MapView, { ProviderPropType } from 'react-native-maps'; import XMarksTheSpot from './CustomOverlayXMarksTheSpot'; @@ -29,7 +25,7 @@ class CustomOverlay extends React.Component { coordinates: [ { longitude: -122.442753, - latitude: 37.798790, + latitude: 37.79879, }, { longitude: -122.424728, diff --git a/example/examples/CustomOverlayXMarksTheSpot.js b/example/examples/CustomOverlayXMarksTheSpot.js index b010e9899..68129d93e 100644 --- a/example/examples/CustomOverlayXMarksTheSpot.js +++ b/example/examples/CustomOverlayXMarksTheSpot.js @@ -19,9 +19,7 @@ class XMarksTheSpot extends React.Component { - + ); } diff --git a/example/examples/CustomTiles.js b/example/examples/CustomTiles.js index 039b62f51..76efde805 100644 --- a/example/examples/CustomTiles.js +++ b/example/examples/CustomTiles.js @@ -1,12 +1,12 @@ import React from 'react'; -import { - StyleSheet, - View, - Text, - Dimensions, -} from 'react-native'; +import { StyleSheet, View, Text, Dimensions } from 'react-native'; -import MapView, { MAP_TYPES, PROVIDER_DEFAULT, ProviderPropType, UrlTile } from 'react-native-maps'; +import MapView, { + MAP_TYPES, + PROVIDER_DEFAULT, + ProviderPropType, + UrlTile, +} from 'react-native-maps'; const { width, height } = Dimensions.get('window'); @@ -32,8 +32,9 @@ class CustomTiles extends React.Component { get mapType() { // MapKit does not support 'none' as a base map - return this.props.provider === PROVIDER_DEFAULT ? - MAP_TYPES.STANDARD : MAP_TYPES.NONE; + return this.props.provider === PROVIDER_DEFAULT + ? MAP_TYPES.STANDARD + : MAP_TYPES.NONE; } render() { diff --git a/example/examples/CustomTilesLocal.js b/example/examples/CustomTilesLocal.js index 0962d1096..f89a43cbf 100644 --- a/example/examples/CustomTilesLocal.js +++ b/example/examples/CustomTilesLocal.js @@ -1,10 +1,5 @@ import React from 'react'; -import { - StyleSheet, - View, - Text, - Dimensions, -} from 'react-native'; +import { StyleSheet, View, Text, Dimensions } from 'react-native'; import MapView, { MAP_TYPES, @@ -37,8 +32,9 @@ class CustomTiles extends React.Component { get mapType() { // MapKit does not support 'none' as a base map - return this.props.provider === PROVIDER_DEFAULT ? - MAP_TYPES.STANDARD : MAP_TYPES.NONE; + return this.props.provider === PROVIDER_DEFAULT + ? MAP_TYPES.STANDARD + : MAP_TYPES.NONE; } render() { diff --git a/example/examples/DefaultMarkers.js b/example/examples/DefaultMarkers.js index 15211a857..dd5ec9ba3 100644 --- a/example/examples/DefaultMarkers.js +++ b/example/examples/DefaultMarkers.js @@ -19,7 +19,9 @@ const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; let id = 0; function randomColor() { - return `#${Math.floor(Math.random() * 16777215).toString(16).padStart(6, 0)}`; + return `#${Math.floor(Math.random() * 16777215) + .toString(16) + .padStart(6, 0)}`; } class DefaultMarkers extends React.Component { @@ -57,7 +59,7 @@ class DefaultMarkers extends React.Component { provider={this.props.provider} style={styles.map} initialRegion={this.state.region} - onPress={(e) => this.onMapPress(e)} + onPress={e => this.onMapPress(e)} > {this.state.markers.map(marker => ( { this.map = ref; }} + ref={ref => { + this.map = ref; + }} mapType={MAP_TYPES.TERRAIN} style={styles.map} initialRegion={this.state.region} onRegionChange={region => this.onRegionChange(region)} /> - + {this.state.region.latitude.toPrecision(7)}, {this.state.region.longitude.toPrecision(7)} @@ -166,6 +170,7 @@ const styles = StyleSheet.create({ buttonText: { textAlign: 'center', }, + centeredText: { textAlign: 'center' }, }); export default DisplayLatLng; diff --git a/example/examples/DraggableMarkers.js b/example/examples/DraggableMarkers.js index 484cd215f..c68123934 100644 --- a/example/examples/DraggableMarkers.js +++ b/example/examples/DraggableMarkers.js @@ -1,9 +1,5 @@ import React from 'react'; -import { - StyleSheet, - View, - Dimensions, -} from 'react-native'; +import { StyleSheet, View, Dimensions } from 'react-native'; import MapView, { Marker, ProviderPropType } from 'react-native-maps'; import PriceMarker from './PriceMarker'; @@ -52,22 +48,22 @@ class MarkerTypes extends React.Component { > log('onSelect', e)} - onDrag={(e) => log('onDrag', e)} - onDragStart={(e) => log('onDragStart', e)} - onDragEnd={(e) => log('onDragEnd', e)} - onPress={(e) => log('onPress', e)} + onSelect={e => log('onSelect', e)} + onDrag={e => log('onDrag', e)} + onDragStart={e => log('onDragStart', e)} + onDragEnd={e => log('onDragEnd', e)} + onPress={e => log('onPress', e)} draggable > log('onSelect', e)} - onDrag={(e) => log('onDrag', e)} - onDragStart={(e) => log('onDragStart', e)} - onDragEnd={(e) => log('onDragEnd', e)} - onPress={(e) => log('onPress', e)} + onSelect={e => log('onSelect', e)} + onDrag={e => log('onDrag', e)} + onDragStart={e => log('onDragStart', e)} + onDragEnd={e => log('onDragEnd', e)} + onPress={e => log('onPress', e)} draggable /> diff --git a/example/examples/EventListener.js b/example/examples/EventListener.js index 26b5b5216..b1f846b0b 100644 --- a/example/examples/EventListener.js +++ b/example/examples/EventListener.js @@ -1,15 +1,15 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { - StyleSheet, - View, - Text, - Dimensions, - ScrollView, -} from 'react-native'; -// eslint-disable-next-line max-len -import MapView, { PROVIDER_GOOGLE, Marker, ProviderPropType, Polygon, Polyline, Callout } from 'react-native-maps'; +import { StyleSheet, View, Text, Dimensions, ScrollView } from 'react-native'; +import MapView, { + PROVIDER_GOOGLE, + Marker, + ProviderPropType, + Polygon, + Polyline, + Callout, +} from 'react-native-maps'; import PriceMarker from './PriceMarker'; const { width, height } = Dimensions.get('window'); @@ -31,7 +31,9 @@ class Event extends React.Component { return ( {event.name} - {JSON.stringify(event.data, null, 2)} + + {JSON.stringify(event.data, null, 2)} + ); } @@ -41,8 +43,6 @@ Event.propTypes = { event: PropTypes.object, }; - -// eslint-disable-next-line react/no-multi-comp class EventListener extends React.Component { constructor(props) { super(props); @@ -69,13 +69,10 @@ class EventListener extends React.Component { recordEvent(name) { return e => { if (e.persist) { - e.persist(); // Avoids warnings relating to https://fb.me/react-event-pooling + e.persist(); // Avoids warnings relating to https://fb.me/react-event-pooling } this.setState(prevState => ({ - events: [ - this.makeEvent(e, name), - ...prevState.events.slice(0, 10), - ], + events: [this.makeEvent(e, name), ...prevState.events.slice(0, 10)], })); }; } @@ -98,7 +95,9 @@ class EventListener extends React.Component { showsUserLocation showsMyLocationButton onRegionChange={this.recordEvent('Map::onRegionChange')} - onRegionChangeComplete={this.recordEvent('Map::onRegionChangeComplete')} + onRegionChangeComplete={this.recordEvent( + 'Map::onRegionChangeComplete' + )} onPress={this.recordEvent('Map::onPress')} onPanDrag={this.recordEvent('Map::onPanDrag')} onLongPress={this.recordEvent('Map::onLongPress')} @@ -110,14 +109,14 @@ class EventListener extends React.Component { > - {this.state.events.map(event => )} + {this.state.events.map(event => ( + + ))} diff --git a/example/examples/FitToCoordinates.js b/example/examples/FitToCoordinates.js index 872d32ae7..95faa9ef2 100644 --- a/example/examples/FitToCoordinates.js +++ b/example/examples/FitToCoordinates.js @@ -20,8 +20,8 @@ const SPACE = 0.01; function createMarker(modifier = 1) { return { - latitude: LATITUDE - (SPACE * modifier), - longitude: LONGITUDE - (SPACE * modifier), + latitude: LATITUDE - SPACE * modifier, + longitude: LONGITUDE - SPACE * modifier, }; } @@ -67,7 +67,9 @@ class FitToCoordinates extends React.Component { return ( { this.map = ref; }} + ref={ref => { + this.map = ref; + }} style={styles.map} initialRegion={{ latitude: LATITUDE, @@ -77,11 +79,7 @@ class FitToCoordinates extends React.Component { }} > {MARKERS.map((marker, i) => ( - + ))} diff --git a/example/examples/FitToSuppliedMarkers.js b/example/examples/FitToSuppliedMarkers.js index 5ae3313a4..5b73deb66 100644 --- a/example/examples/FitToSuppliedMarkers.js +++ b/example/examples/FitToSuppliedMarkers.js @@ -1,9 +1,5 @@ import React from 'react'; -import { - StyleSheet, - View, - Dimensions, -} from 'react-native'; +import { StyleSheet, View, Dimensions } from 'react-native'; import MapView, { Marker, ProviderPropType } from 'react-native-maps'; @@ -34,16 +30,16 @@ class FocusOnMarkers extends React.Component { longitude: LONGITUDE - SPACE, }, c: { - latitude: LATITUDE - (SPACE * 2), - longitude: LONGITUDE - (SPACE * 2), + latitude: LATITUDE - SPACE * 2, + longitude: LONGITUDE - SPACE * 2, }, d: { - latitude: LATITUDE - (SPACE * 3), - longitude: LONGITUDE - (SPACE * 3), + latitude: LATITUDE - SPACE * 3, + longitude: LONGITUDE - SPACE * 3, }, e: { - latitude: LATITUDE - (SPACE * 4), - longitude: LONGITUDE - (SPACE * 4), + latitude: LATITUDE - SPACE * 4, + longitude: LONGITUDE - SPACE * 4, }, }; } @@ -67,10 +63,7 @@ class FocusOnMarkers extends React.Component { focus1() { animationTimeout = setTimeout(() => { - this.focusMap([ - markerIDs[1], - markerIDs[4], - ], true); + this.focusMap([markerIDs[1], markerIDs[4]], true); this.focus2(); }, timeout); @@ -78,10 +71,7 @@ class FocusOnMarkers extends React.Component { focus2() { animationTimeout = setTimeout(() => { - this.focusMap([ - markerIDs[2], - markerIDs[3], - ], false); + this.focusMap([markerIDs[2], markerIDs[3]], false); this.focus3(); }, timeout); @@ -89,10 +79,7 @@ class FocusOnMarkers extends React.Component { focus3() { animationTimeout = setTimeout(() => { - this.focusMap([ - markerIDs[1], - markerIDs[2], - ], false); + this.focusMap([markerIDs[1], markerIDs[2]], false); this.focus4(); }, timeout); @@ -100,10 +87,7 @@ class FocusOnMarkers extends React.Component { focus4() { animationTimeout = setTimeout(() => { - this.focusMap([ - markerIDs[0], - markerIDs[3], - ], true); + this.focusMap([markerIDs[0], markerIDs[3]], true); this.focus1(); }, timeout); @@ -114,7 +98,9 @@ class FocusOnMarkers extends React.Component { { this.map = ref; }} + ref={ref => { + this.map = ref; + }} style={styles.map} initialRegion={{ latitude: LATITUDE, @@ -123,26 +109,11 @@ class FocusOnMarkers extends React.Component { longitudeDelta: LONGITUDE_DELTA, }} > - - - - - + + + + + ); diff --git a/example/examples/GradientPolylines.js b/example/examples/GradientPolylines.js index 4ab44d26d..14313bd50 100644 --- a/example/examples/GradientPolylines.js +++ b/example/examples/GradientPolylines.js @@ -1,8 +1,5 @@ import React from 'react'; -import { - StyleSheet, - Dimensions, -} from 'react-native'; +import { StyleSheet, Dimensions } from 'react-native'; import MapView, { Polyline, ProviderPropType } from 'react-native-maps'; diff --git a/example/examples/ImageOverlayWithAssets.js b/example/examples/ImageOverlayWithAssets.js index d9fcfe77e..1f2f43ef1 100644 --- a/example/examples/ImageOverlayWithAssets.js +++ b/example/examples/ImageOverlayWithAssets.js @@ -1,9 +1,5 @@ import React, { Component } from 'react'; -import { - StyleSheet, - View, - Dimensions, -} from 'react-native'; +import { StyleSheet, View, Dimensions } from 'react-native'; import MapView from 'react-native-maps'; import flagPinkImg from './assets/flag-pink.png'; @@ -21,7 +17,6 @@ const OVERLAY_BOTTOM_RIGHT_COORDINATE = [35.679609609368576, 139.76806640625]; const IMAGE = flagPinkImg; export default class ImageOverlayWithURL extends Component { - static propTypes = { provider: MapView.ProviderPropType, }; diff --git a/example/examples/ImageOverlayWithURL.js b/example/examples/ImageOverlayWithURL.js index e4d7ad8de..e907e2097 100644 --- a/example/examples/ImageOverlayWithURL.js +++ b/example/examples/ImageOverlayWithURL.js @@ -1,9 +1,5 @@ import React, { Component } from 'react'; -import { - StyleSheet, - View, - Dimensions, -} from 'react-native'; +import { StyleSheet, View, Dimensions } from 'react-native'; import MapView from 'react-native-maps'; @@ -24,7 +20,6 @@ const OVERLAY_BOTTOM_RIGHT_COORDINATE2 = [35.67514743608467, 139.76806640625]; const IMAGE_URL2 = 'https://maps.gsi.go.jp/xyz/std/17/116423/51615.png'; export default class ImageOverlayWithURL extends Component { - static propTypes = { provider: MapView.ProviderPropType, }; @@ -40,11 +35,17 @@ export default class ImageOverlayWithURL extends Component { longitudeDelta: LONGITUDE_DELTA, }, overlay1: { - bounds: [OVERLAY_TOP_LEFT_COORDINATE1, OVERLAY_BOTTOM_RIGHT_COORDINATE1], + bounds: [ + OVERLAY_TOP_LEFT_COORDINATE1, + OVERLAY_BOTTOM_RIGHT_COORDINATE1, + ], image: IMAGE_URL1, }, overlay2: { - bounds: [OVERLAY_TOP_LEFT_COORDINATE2, OVERLAY_BOTTOM_RIGHT_COORDINATE2], + bounds: [ + OVERLAY_TOP_LEFT_COORDINATE2, + OVERLAY_BOTTOM_RIGHT_COORDINATE2, + ], image: IMAGE_URL2, }, }; diff --git a/example/examples/IndoorMap.js b/example/examples/IndoorMap.js index 01f3911af..ad75013d8 100644 --- a/example/examples/IndoorMap.js +++ b/example/examples/IndoorMap.js @@ -20,10 +20,7 @@ class IndoorMap extends React.Component { const { defaultLevelIndex, levels } = indoorBuilding; const levelNames = levels.map(lv => lv.name || ''); const msg = `Default Level: ${defaultLevelIndex}\nLevels: ${levelNames.toString()}`; - Alert.alert( - 'Indoor building focused', - msg - ); + Alert.alert('Indoor building focused', msg); } setIndoorLevel(level) { @@ -45,10 +42,22 @@ class IndoorMap extends React.Component { showsIndoors showsIndoorLevelPicker onIndoorBuildingFocused={this.handleIndoorFocus} - ref={map => { this.map = map; }} + ref={map => { + this.map = map; + }} + /> +