diff --git a/.project b/.project
new file mode 100644
index 000000000..f564041e5
--- /dev/null
+++ b/.project
@@ -0,0 +1,17 @@
+
+
+ react-native-maps
+ Project react-native-maps created by Buildship.
+
+
+
+
+ org.eclipse.buildship.core.gradleprojectbuilder
+
+
+
+
+
+ org.eclipse.buildship.core.gradleprojectnature
+
+
diff --git a/.settings/org.eclipse.buildship.core.prefs b/.settings/org.eclipse.buildship.core.prefs
new file mode 100644
index 000000000..03931c0c1
--- /dev/null
+++ b/.settings/org.eclipse.buildship.core.prefs
@@ -0,0 +1,3 @@
+connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
+connection.project.dir=
+eclipse.preferences.version=1
diff --git a/build.gradle b/build.gradle
index 8a3e2453e..588e5cb99 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.3.1'
+ classpath 'com.android.tools.build:gradle:2.3.2'
}
}
diff --git a/example/App.js b/example/App.js
index dadbea056..793a0f809 100644
--- a/example/App.js
+++ b/example/App.js
@@ -123,6 +123,7 @@ class App extends React.Component {
render() {
return this.renderExamples([
// [, , , ]
+ [CustomTiles, 'Custom Tiles', true],
[StaticMap, 'StaticMap', true],
[DisplayLatLng, 'Tracking Position', true, '(incomplete)'],
[ViewsAsMarkers, 'Arbitrary Views as Markers', true],
@@ -143,7 +144,6 @@ class App extends React.Component {
[FitToSuppliedMarkers, 'Focus Map On Markers', true],
[FitToCoordinates, 'Fit Map To Coordinates', true],
[LiteMapView, 'Android Lite MapView'],
- [CustomTiles, 'Custom 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/android/app/.classpath b/example/android/app/.classpath
new file mode 100644
index 000000000..8d8d85f14
--- /dev/null
+++ b/example/android/app/.classpath
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/example/android/app/.project b/example/android/app/.project
new file mode 100644
index 000000000..ca3856c62
--- /dev/null
+++ b/example/android/app/.project
@@ -0,0 +1,23 @@
+
+
+ example-android
+ Project example-android created by Buildship.
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.buildship.core.gradleprojectbuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ org.eclipse.buildship.core.gradleprojectnature
+
+
diff --git a/example/android/app/.settings/org.eclipse.buildship.core.prefs b/example/android/app/.settings/org.eclipse.buildship.core.prefs
new file mode 100644
index 000000000..0d766b113
--- /dev/null
+++ b/example/android/app/.settings/org.eclipse.buildship.core.prefs
@@ -0,0 +1,3 @@
+connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
+connection.project.dir=../../..
+eclipse.preferences.version=1
diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml
index b111eef4a..659a1fcdc 100644
--- a/example/android/app/src/main/AndroidManifest.xml
+++ b/example/android/app/src/main/AndroidManifest.xml
@@ -3,6 +3,9 @@
+
+
+
{
+ this.updateRegion(region);
+ }
+
+ onRegionChange = (region) => {
+ if (this.onRegionChangeTimer) {
+ clearTimeout(this.onRegionChangeTimer);
+ }
+ this.onRegionChangeTimer = setTimeout(() => {
+ this.updateRegion(region);
+ }, 200);
+ }
+
+ onMapPress = (e) => {
+ const coordinates = e.nativeEvent.coordinate;
+ const zoom = this.getMapZoom();
+ const tile = tilebelt.pointToTile(coordinates.longitude, coordinates.latitude, zoom, true);
+
+ this.setState({
+ coordinates: {
+ tile: [tile[0], tile[1], tile[2]],
+ precision: [tile[3], tile[4]],
+ },
+ });
+ }
+
+ updateRegion = (region) => {
+ this.setState({ region });
+ }
+
render() {
- const { region } = this.state;
+ const { region, coordinates } = this.state;
+ const hasCoordinates = (coordinates.tile && coordinates.tile.length === 3) || false;
+
return (
-
+ {hasCoordinates &&
+
+ }
diff --git a/lib/android/.classpath b/lib/android/.classpath
new file mode 100644
index 000000000..8d8d85f14
--- /dev/null
+++ b/lib/android/.classpath
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/lib/android/.project b/lib/android/.project
new file mode 100644
index 000000000..2a84911fc
--- /dev/null
+++ b/lib/android/.project
@@ -0,0 +1,23 @@
+
+
+ react-native-maps-lib
+ Project react-native-maps-lib created by Buildship.
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.buildship.core.gradleprojectbuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ org.eclipse.buildship.core.gradleprojectnature
+
+
diff --git a/lib/android/.settings/org.eclipse.buildship.core.prefs b/lib/android/.settings/org.eclipse.buildship.core.prefs
new file mode 100644
index 000000000..abb1f3437
--- /dev/null
+++ b/lib/android/.settings/org.eclipse.buildship.core.prefs
@@ -0,0 +1,3 @@
+connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
+connection.project.dir=../..
+eclipse.preferences.version=1
diff --git a/lib/android/gradle.properties b/lib/android/gradle.properties
index 65c67798d..307005f30 100644
--- a/lib/android/gradle.properties
+++ b/lib/android/gradle.properties
@@ -16,3 +16,4 @@ POM_DEVELOPER_NAME=Leland Richardson
POM_NAME=ReactNative Maps library
POM_ARTIFACT_ID=react-native-maps
POM_PACKAGING=aar
+org.gradle.jvmargs=-Xmx2048m
diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCanvasFeature.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCanvasFeature.java
new file mode 100644
index 000000000..ba1b877dc
--- /dev/null
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCanvasFeature.java
@@ -0,0 +1,131 @@
+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;
+
+/**
+ * Created by joseangel.parreno@vizzuality.com on 25/05/2017.
+ */
+
+public abstract class AirMapCanvasFeature extends AirMapFeature {
+ protected TileOverlayOptions tileOverlayOptions;
+ protected TileOverlay tileOverlay;
+ protected AirMapCanvasTileProvider tileProvider;
+
+ protected String urlTemplate;
+ protected int maxZoom;
+ protected String areaId;
+ protected String alertType;
+ protected String minDate;
+ protected String maxDate;
+ protected boolean isConnected;
+ protected float zIndex;
+ protected Coordinates coordinates;
+
+ public AirMapCanvasFeature(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 setMaxZoom(int maxZoom) {
+ this.maxZoom = maxZoom;
+ if (tileProvider != null) {
+ tileProvider.setMaxZoom(maxZoom);
+ }
+ if (tileOverlay != null) {
+ tileOverlay.clearTileCache();
+ }
+ }
+
+ public void setAreaId(String areaId) {
+ this.areaId = areaId;
+ if (tileProvider != null) {
+ tileProvider.setAreaId(areaId);
+ }
+ if (tileOverlay != null) {
+ tileOverlay.clearTileCache();
+ }
+ }
+
+ public void setIsConnected(boolean isConnected) {
+ this.isConnected = isConnected;
+ if (tileProvider != null) {
+ tileProvider.setIsConnected(isConnected);
+ }
+ if (tileOverlay != null) {
+ tileOverlay.clearTileCache();
+ }
+ }
+
+ public void setAlertType(String alertType) {
+ this.alertType = alertType;
+ if (tileProvider != null) {
+ tileProvider.setAlertType(alertType);
+ }
+ if (tileOverlay != null) {
+ tileOverlay.clearTileCache();
+ }
+ }
+
+ public void setMinDate(String minDate) {
+ this.minDate = minDate;
+ }
+
+ public void setMaxDate(String maxDate) {
+ this.maxDate = maxDate;
+ }
+
+ public void setZIndex(float zIndex) {
+ this.zIndex = zIndex;
+ if (tileOverlay != null) {
+ tileOverlay.setZIndex(zIndex);
+ }
+ }
+
+ public void setCoordinates(Coordinates coordinates) {
+ this.coordinates = coordinates;
+ if (tileProvider != null) {
+ tileProvider.setCoordinates(coordinates);
+ }
+ if (tileOverlay != null) {
+ tileOverlay.clearTileCache();
+ }
+ }
+
+ public TileOverlayOptions getTileOverlayOptions() {
+ if (tileOverlayOptions == null) {
+ tileOverlayOptions = createTileOverlayOptions();
+ }
+ return tileOverlayOptions;
+ }
+
+ protected abstract TileOverlayOptions createTileOverlayOptions();
+
+
+ @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/AirMapCanvasInteractionUrlTile.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCanvasInteractionUrlTile.java
new file mode 100644
index 000000000..261bce835
--- /dev/null
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCanvasInteractionUrlTile.java
@@ -0,0 +1,82 @@
+package com.airbnb.android.react.maps;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import com.google.android.gms.maps.model.TileOverlayOptions;
+
+
+public class AirMapCanvasInteractionUrlTile extends AirMapCanvasFeature {
+
+ public AirMapCanvasInteractionUrlTile(Context context){ super(context); }
+
+ class AIRMapCanvasInteractionTileProvider extends AirMapCanvasTileProvider {
+
+ public AIRMapCanvasInteractionTileProvider(int width, int height, String urlTemplate, int maxZoom, String areaId, boolean isConnected, String minDate, String maxDate, String alertType, Coordinates coordinates) {
+ super(width, height, urlTemplate, maxZoom, areaId, isConnected, minDate, maxDate, alertType, coordinates);
+ }
+
+ protected Context getParentContext() {
+ return getContext();
+ }
+
+ protected Bitmap paintTile(Bitmap scaledBitmap, int width, int height, int zoom, int zsteps, int minDate, int maxDate) {
+ Bitmap finalBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ int red, green, blue, alpha, c;
+ double[] precision = coordinates.getPrecision();
+ int xFilter = (int)(precision[0] * width);
+ int yFilter = (int)(precision[1] * height);
+ double THRESHOLD = zoom * 0.5 * zsteps;
+
+
+ for(int xPoint=0; xPoint < width; xPoint++) {
+ for(int yPoint=0; yPoint < height; yPoint++) {
+ c = scaledBitmap.getPixel(xPoint, yPoint);
+
+ red = Color.red(c);
+ green = Color.green(c);
+ blue = Color.blue(c);
+
+ if (red > 255) red = 255;
+ if (green > 255) green = 255;
+ if (blue > 255) blue = 255;
+
+ int day;
+ if (this.alertType != null && this.alertType.equals("viirs")) {
+ day = blue;
+ } else {
+ day = red * 255 + green;
+ }
+
+ boolean inDay = day > 0 && day >= minDate && day <= maxDate;
+ boolean inXPoint = xPoint >= (xFilter - THRESHOLD) && (xPoint <= xFilter + THRESHOLD);
+ boolean inYPoint = yPoint >= (yFilter - THRESHOLD) && (yPoint <= yFilter + THRESHOLD);
+
+ if (inDay && inXPoint && inYPoint) {
+ red = 255;
+ green = 255;
+ blue = 255;
+ alpha = 150;
+ } else {
+ alpha = 0;
+ }
+
+ finalBitmap.setPixel(xPoint, yPoint, Color.argb(alpha, red, green, blue));
+ }
+ }
+ return finalBitmap;
+ }
+
+
+ }
+
+
+ protected TileOverlayOptions createTileOverlayOptions() {
+ TileOverlayOptions options = new TileOverlayOptions();
+ options.zIndex(zIndex);
+ this.tileProvider = new AIRMapCanvasInteractionTileProvider(256, 256, this.urlTemplate, this.maxZoom, this.areaId, this.isConnected, this.minDate, this.maxDate, this.alertType, this.coordinates);
+ options.tileProvider(this.tileProvider);
+ return options;
+ }
+
+}
diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCanvasInteractionUrlTileManager.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCanvasInteractionUrlTileManager.java
new file mode 100644
index 000000000..e4fc03957
--- /dev/null
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCanvasInteractionUrlTileManager.java
@@ -0,0 +1,84 @@
+package com.airbnb.android.react.maps;
+
+import android.content.Context;
+import android.os.Build;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.WindowManager;
+
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.bridge.ReadableMap;
+import com.facebook.react.uimanager.ThemedReactContext;
+import com.facebook.react.uimanager.ViewGroupManager;
+import com.facebook.react.uimanager.annotations.ReactProp;
+
+public class AirMapCanvasInteractionUrlTileManager extends ViewGroupManager {
+ private DisplayMetrics metrics;
+
+ public AirMapCanvasInteractionUrlTileManager(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 "AIRMapCanvasInteractionUrlTile";
+ }
+
+ @Override
+ public AirMapCanvasInteractionUrlTile createViewInstance(ThemedReactContext context) {
+ return new AirMapCanvasInteractionUrlTile(context);
+ }
+
+ @ReactProp(name = "urlTemplate")
+ public void setUrlTemplate(AirMapCanvasInteractionUrlTile view, String urlTemplate) {
+ view.setUrlTemplate(urlTemplate);
+ }
+
+ @ReactProp(name = "maxZoom", defaultInt = 12)
+ public void setMaxZoom(AirMapCanvasInteractionUrlTile view, int maxZoom) {
+ view.setMaxZoom(maxZoom);
+ }
+
+ @ReactProp(name = "areaId")
+ public void setAreaId(AirMapCanvasInteractionUrlTile view, String areaId) {
+ view.setAreaId(areaId);
+ }
+
+ @ReactProp(name = "isConnected", defaultBoolean = true)
+ public void setIsConnected(AirMapCanvasInteractionUrlTile view, boolean isConnected) {
+ view.setIsConnected(isConnected);
+ }
+
+ @ReactProp(name = "minDate")
+ public void setMinDate(AirMapCanvasInteractionUrlTile view, String minDate) {
+ view.setMinDate(minDate);
+ }
+
+ @ReactProp(name = "maxDate")
+ public void setMaxDate(AirMapCanvasInteractionUrlTile view, String maxDate) {
+ view.setMaxDate(maxDate);
+ }
+
+ @ReactProp(name = "zIndex", defaultFloat = -1.0f)
+ public void setZIndex(AirMapCanvasInteractionUrlTile view, float zIndex) {
+ view.setZIndex(zIndex);
+ }
+
+ @ReactProp(name = "coordinates")
+ public void setCoordinates(AirMapCanvasInteractionUrlTile view, ReadableMap coordinates) {
+ if (coordinates != null) {
+ int[] tile = new int[]{coordinates.getArray("tile").getInt(0), coordinates.getArray("tile").getInt(1), coordinates.getArray("tile").getInt(2)};
+ double[] precision = new double[]{coordinates.getArray("precision").getDouble(0), coordinates.getArray("precision").getDouble(1)};
+ view.setCoordinates(new Coordinates(tile, precision));
+ }
+ }
+
+}
diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCanvasTileProvider.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCanvasTileProvider.java
new file mode 100644
index 000000000..0f9be1ca0
--- /dev/null
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCanvasTileProvider.java
@@ -0,0 +1,180 @@
+package com.airbnb.android.react.maps;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.util.Log;
+
+import com.google.android.gms.maps.model.Tile;
+import com.google.android.gms.maps.model.TileProvider;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.net.URL;
+
+/**
+ * Created by joseangel.parreno@vizzuality.com on 19/05/2017.
+ */
+
+
+public abstract class AirMapCanvasTileProvider implements TileProvider {
+ protected String urlTemplate;
+ protected int width;
+ protected int height;
+ protected int maxZoom;
+ protected String areaId;
+ protected String minDate;
+ protected String maxDate;
+ protected boolean isConnected;
+ protected Coordinates coordinates;
+ protected String alertType;
+
+ public AirMapCanvasTileProvider(int width, int height, String urlTemplate, int maxZoom, String areaId, boolean isConnected, String minDate, String maxDate, String alertType, Coordinates coordinates) {
+ super();
+ this.width = width;
+ this.height = height;
+ this.urlTemplate = urlTemplate;
+ this.maxZoom = maxZoom;
+ this.areaId = areaId;
+ this.minDate = minDate;
+ this.maxDate = maxDate;
+ this.alertType = alertType;
+ this.isConnected = isConnected;
+ this.coordinates = coordinates;
+ }
+ @Override
+ public Tile getTile(int x, int y, int zoom) {
+ int TILE_SIZE = this.width;
+ int maxZoom = 12;
+ int xCord = x;
+ int yCord = y;
+ int zoomCord = zoom;
+ int srcX = 0;
+ int srcY = 0;
+ int srcW = this.width;
+ int srcH = this.height;
+ int scaleSize = 1;
+
+ int minDate = Integer.valueOf(this.minDate);
+ int maxDate = Integer.valueOf(this.maxDate);
+
+ if (this.coordinates != null) {
+ int[] tile = this.coordinates.getTile();
+ if(!(tile[0] == x && tile[1] == y && tile[2] == zoom)) {
+ return NO_TILE;
+ }
+ }
+
+ if (zoom > this.maxZoom) {
+ xCord = (int)(x / (Math.pow(2, zoom - this.maxZoom)));
+ yCord = (int)(y / (Math.pow(2, zoom - this.maxZoom)));
+ zoomCord = this.maxZoom;
+ }
+
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ byte[] bitmapData = stream.toByteArray();
+
+ Bitmap image;
+
+ if (this.isConnected) {
+ String providerUrl = this.urlTemplate
+ .replace("{x}", Integer.toString(xCord))
+ .replace("{y}", Integer.toString(yCord))
+ .replace("{z}", Integer.toString(zoomCord));
+
+ URL url;
+ try {
+ url = new URL(providerUrl);
+ image = BitmapFactory.decodeStream(url.openConnection().getInputStream());
+ } catch (Exception e) {
+ e.printStackTrace();
+ return NO_TILE;
+ }
+
+ } else {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inPreferredConfig = Bitmap.Config.ARGB_8888;
+ File dir = getParentContext().getFilesDir();
+ File myFile = new File(dir + "/tiles", areaId + "/" + zoomCord + "x" + xCord + "x" + yCord + ".png");
+
+ try {
+ image = BitmapFactory.decodeFile(myFile.getAbsolutePath(), options);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return NO_TILE;
+ }
+ }
+ int zsteps = 1;
+
+ if (zoom > this.maxZoom) {
+ zsteps = zoom - this.maxZoom;
+ int relation = (int) Math.pow(2, zsteps) ;
+ int size = (int) (TILE_SIZE / relation);
+ // we scale the map to keep the tiles sharp
+ scaleSize = (int) (TILE_SIZE * 2);
+ srcX = (int) size * (x % relation);
+ srcY = (int) size * (y % relation);
+ srcW = (int) size;
+ srcH = (int) size;
+ }
+
+ if (image != null) {
+ Bitmap croppedBitmap = Bitmap.createBitmap(image , srcX , srcY, srcW, srcH);
+ Bitmap scaledBitmap = croppedBitmap;
+ if (zoom > maxZoom) {
+ // The last false is for filter anti-aliasing
+ scaledBitmap = Bitmap.createScaledBitmap (croppedBitmap, scaleSize, scaleSize, false);
+ }
+
+ int width, height;
+ height = scaledBitmap.getHeight();
+ width = scaledBitmap.getWidth();
+
+ //
+
+ int[] pixels = new int[width * height];
+ scaledBitmap.getPixels(pixels, 0, width, 0, 0, width, height);
+
+
+ Bitmap finalBitmap = paintTile(scaledBitmap, width, height, zoom, zsteps, minDate, maxDate);
+
+
+
+ stream = new ByteArrayOutputStream();
+ finalBitmap.compress(Bitmap.CompressFormat.PNG, 0, stream);
+ bitmapData = stream.toByteArray();
+ return new Tile(TILE_SIZE, TILE_SIZE, bitmapData);
+ } else {
+ return NO_TILE;
+ }
+ }
+
+ protected abstract Context getParentContext();
+
+ protected abstract Bitmap paintTile(Bitmap scaledBitmap, int width, int height, int zoom, int zsteps, int minDate, int maxDate);
+
+ public void setUrlTemplate(String urlTemplate) {
+ this.urlTemplate = urlTemplate;
+ }
+
+ public void setMaxZoom(int maxZoom) {
+ this.maxZoom = maxZoom;
+ }
+
+ public void setAreaId(String areaId) {
+ this.areaId = areaId;
+ }
+
+ public void setAlertType(String alertType) {
+ this.alertType = alertType;
+ }
+
+ public void setIsConnected(boolean isConnected) {
+ this.isConnected = isConnected;
+ }
+
+ public void setCoordinates(Coordinates coordinates) {
+ this.coordinates = coordinates;
+ }
+}
diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCanvasUrlTile.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCanvasUrlTile.java
new file mode 100644
index 000000000..bac7b824f
--- /dev/null
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCanvasUrlTile.java
@@ -0,0 +1,82 @@
+package com.airbnb.android.react.maps;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import com.google.android.gms.maps.model.TileOverlayOptions;
+
+
+public class AirMapCanvasUrlTile extends AirMapCanvasFeature {
+
+ public AirMapCanvasUrlTile(Context context){ super(context); }
+
+ class AIRMapCanvasUrlTileProvider extends AirMapCanvasTileProvider {
+
+ public AIRMapCanvasUrlTileProvider(int width, int height, String urlTemplate, int maxZoom, String areaId, boolean isConnected, String minDate, String maxDate, String alertType, Coordinates coordinates) {
+ super(width, height, urlTemplate, maxZoom, areaId, isConnected, minDate, maxDate, alertType, coordinates);
+ }
+
+ protected Context getParentContext() {
+ return getContext();
+ }
+
+ protected Bitmap paintTile(Bitmap scaledBitmap, int width, int height, int zoom, int zsteps, int minDate, int maxDate) {
+ Bitmap finalBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ int red, green, blue, pixel, alpha;
+ int[] pixels = new int[width * height];
+ scaledBitmap.getPixels(pixels, 0, width, 0, 0, width, height);
+ for (int i = 0; i < pixels.length; i++) {
+ pixel = pixels[i];
+
+ red = (pixel >> 16) & 0xFF;
+ green = (pixel >> 8) & 0xFF;
+ blue = pixel & 0xFF;
+
+ if (red > 255) red = 255;
+ if (green > 255) green = 255;
+ if (blue > 255) blue = 255;
+
+ int day;
+ if (this.alertType != null && this.alertType.equals("viirs")) {
+ day = blue;
+ } else {
+ day = red * 255 + green;
+ }
+
+ if(this.alertType != null && this.alertType.equals("viirs")) {
+ if (day > 0 && day >= minDate && day <= maxDate) {
+ red = 244;
+ green = 66;
+ blue = 66;
+ alpha = 255;
+ } else {
+ alpha = 0;
+ }
+ } else if (day > 0 && day >= minDate && day <= maxDate) {
+ red = 220;
+ green = 102;
+ blue = 153;
+ alpha = 255;
+ } else {
+ alpha = 0;
+ }
+
+ pixels[i] = Color.argb(alpha, red, green, blue);
+ }
+ finalBitmap.setPixels(pixels, 0, width, 0, 0, width, height);
+ return finalBitmap;
+ }
+
+
+ }
+
+
+ protected TileOverlayOptions createTileOverlayOptions() {
+ TileOverlayOptions options = new TileOverlayOptions();
+ options.zIndex(zIndex);
+ this.tileProvider = new AIRMapCanvasUrlTileProvider(256, 256, this.urlTemplate, this.maxZoom, this.areaId, this.isConnected, this.minDate, this.maxDate, this.alertType, null);
+ options.tileProvider(this.tileProvider);
+ return options;
+ }
+
+}
diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCanvasUrlTileManager.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCanvasUrlTileManager.java
new file mode 100644
index 000000000..b15ccbb37
--- /dev/null
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCanvasUrlTileManager.java
@@ -0,0 +1,79 @@
+package com.airbnb.android.react.maps;
+
+import android.content.Context;
+import android.os.Build;
+import android.util.DisplayMetrics;
+import android.util.Log;
+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 AirMapCanvasUrlTileManager extends ViewGroupManager {
+ private DisplayMetrics metrics;
+
+ public AirMapCanvasUrlTileManager(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 "AIRMapCanvasUrlTile";
+ }
+
+ @Override
+ public AirMapCanvasUrlTile createViewInstance(ThemedReactContext context) {
+ return new AirMapCanvasUrlTile(context);
+ }
+
+ @ReactProp(name = "urlTemplate")
+ public void setUrlTemplate(AirMapCanvasUrlTile view, String urlTemplate) {
+ view.setUrlTemplate(urlTemplate);
+ }
+
+ @ReactProp(name = "maxZoom", defaultInt = 12)
+ public void setMaxZoom(AirMapCanvasUrlTile view, int maxZoom) {
+ view.setMaxZoom(maxZoom);
+ }
+
+ @ReactProp(name = "alertType")
+ public void setAlertType(AirMapCanvasUrlTile view, String alertType) {
+ view.setAlertType(alertType);
+ }
+
+ @ReactProp(name = "areaId")
+ public void setAreaId(AirMapCanvasUrlTile view, String areaId) {
+ view.setAreaId(areaId);
+ }
+
+ @ReactProp(name = "isConnected", defaultBoolean = true)
+ public void setIsConnected(AirMapCanvasUrlTile view, boolean isConnected) {
+ view.setIsConnected(isConnected);
+ }
+
+ @ReactProp(name = "minDate")
+ public void setMinDate(AirMapCanvasUrlTile view, String minDate) {
+ view.setMinDate(minDate);
+ }
+
+ @ReactProp(name = "maxDate")
+ public void setMaxDate(AirMapCanvasUrlTile view, String maxDate) {
+ view.setMaxDate(maxDate);
+ }
+
+ @ReactProp(name = "zIndex", defaultFloat = -1.0f)
+ public void setZIndex(AirMapCanvasUrlTile view, float zIndex) {
+ view.setZIndex(zIndex);
+ }
+
+}
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 3225bf1ff..a4610efff 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
@@ -12,6 +12,7 @@
import android.os.Handler;
import android.support.v4.view.GestureDetectorCompat;
import android.support.v4.view.MotionEventCompat;
+import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
@@ -462,6 +463,7 @@ public void setHandlePanDrag(boolean handlePanDrag) {
public void addFeature(View child, int index) {
// Our desired API is to pass up annotations/overlays as children to the mapview component.
// This is where we intercept them and do the appropriate underlying mapview action.
+
if (child instanceof AirMapMarker) {
AirMapMarker annotation = (AirMapMarker) child;
annotation.addToMap(map);
@@ -488,6 +490,14 @@ public void addFeature(View child, int index) {
AirMapUrlTile urlTileView = (AirMapUrlTile) child;
urlTileView.addToMap(map);
features.add(index, urlTileView);
+ } else if (child instanceof AirMapCanvasUrlTile) {
+ AirMapCanvasUrlTile canvasUrlTileView = (AirMapCanvasUrlTile) child;
+ canvasUrlTileView.addToMap(map);
+ features.add(index, canvasUrlTileView);
+ } else if (child instanceof AirMapCanvasInteractionUrlTile) {
+ AirMapCanvasInteractionUrlTile canvasInteractionUrlTileView = (AirMapCanvasInteractionUrlTile) child;
+ canvasInteractionUrlTileView.addToMap(map);
+ features.add(index, canvasInteractionUrlTileView);
} else {
ViewGroup children = (ViewGroup) child;
for (int i = 0; i < children.getChildCount(); i++) {
diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/Coordinates.java b/lib/android/src/main/java/com/airbnb/android/react/maps/Coordinates.java
new file mode 100644
index 000000000..5de7247d7
--- /dev/null
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/Coordinates.java
@@ -0,0 +1,22 @@
+package com.airbnb.android.react.maps;
+
+/**
+ * Created by joseangel on 25/05/2017.
+ */
+
+public class Coordinates {
+ private int[] tile;
+ private double[] precision;
+
+ public Coordinates(int[] tile, double[] precision) {
+ this.tile = tile;
+ this.precision = precision;
+ }
+
+ public int[] getTile() {
+ return tile;
+ }
+ public double[] getPrecision() {
+ return precision;
+ }
+}
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 e76f63aa2..5284e1581 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,8 @@ public List createViewManagers(ReactApplicationContext reactContext
AirMapManager mapManager = new AirMapManager(reactContext);
AirMapLiteManager mapLiteManager = new AirMapLiteManager(reactContext);
AirMapUrlTileManager tileManager = new AirMapUrlTileManager(reactContext);
+ AirMapCanvasUrlTileManager canvasTileManager = new AirMapCanvasUrlTileManager(reactContext);
+ AirMapCanvasInteractionUrlTileManager canvasInteractionTileManager = new AirMapCanvasInteractionUrlTileManager(reactContext);
return Arrays.asList(
calloutManager,
@@ -48,6 +50,8 @@ public List createViewManagers(ReactApplicationContext reactContext
circleManager,
mapManager,
mapLiteManager,
- tileManager);
+ tileManager,
+ canvasTileManager,
+ canvasInteractionTileManager);
}
}
diff --git a/lib/components/MapCanvasInteractionUrlTile.js b/lib/components/MapCanvasInteractionUrlTile.js
new file mode 100644
index 000000000..cafddfa06
--- /dev/null
+++ b/lib/components/MapCanvasInteractionUrlTile.js
@@ -0,0 +1,90 @@
+import React, { PropTypes } from 'react';
+
+import {
+ View,
+} from 'react-native';
+
+import decorateMapComponent, {
+ USES_DEFAULT_IMPLEMENTATION,
+ SUPPORTED,
+} from './decorateMapComponent';
+
+const viewConfig = {
+ uiViewClassName: 'AIRCanvasInteractionUrlTile',
+};
+
+const propTypes = {
+ ...View.propTypes,
+
+ /**
+ * The url template of the tile server. The patterns {x} {y} {z} will be replaced at runtime
+ * For example, http://c.tile.openstreetmap.org/{z}/{x}/{y}.png
+ */
+ 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,
+ /**
+ * Flag to use the offline tiles instead of the url version
+ */
+ isConnected: PropTypes.bool,
+ /**
+ * Area id to get the tiles from the corrent folder
+ */
+ areaId: PropTypes.string,
+ /**
+ * Min date to get tiles
+ */
+ minDate: PropTypes.string,
+ /**
+ * Max date to get tiles
+ */
+ maxDate: PropTypes.string,
+ /**
+ * Max zoom when the tiles have data
+ */
+ maxZoom: PropTypes.number,
+ /**
+ * Max zoom when the tiles have data
+ */
+ coordinates: PropTypes.shape({
+ tile: PropTypes.arrayOf(React.PropTypes.number),
+ precision: PropTypes.arrayOf(React.PropTypes.number),
+ }),
+};
+
+const defaultProps = {
+ maxZoom: 12,
+ isConnected: true,
+};
+
+class CanvasInteractionUrlTile extends React.Component {
+ render() {
+ const AIRMapCanvasInteractionUrlTile = this.getAirComponent();
+ return (
+
+ );
+ }
+}
+
+CanvasInteractionUrlTile.viewConfig = viewConfig;
+CanvasInteractionUrlTile.propTypes = propTypes;
+CanvasInteractionUrlTile.defaultProps = defaultProps;
+
+module.exports = decorateMapComponent(CanvasInteractionUrlTile, {
+ componentType: 'CanvasInteractionUrlTile',
+ providers: {
+ google: {
+ ios: SUPPORTED,
+ android: USES_DEFAULT_IMPLEMENTATION,
+ },
+ },
+});
diff --git a/lib/components/MapCanvasUrlTile.js b/lib/components/MapCanvasUrlTile.js
new file mode 100644
index 000000000..49a5509c8
--- /dev/null
+++ b/lib/components/MapCanvasUrlTile.js
@@ -0,0 +1,87 @@
+import React, { PropTypes } from 'react';
+
+import {
+ View,
+} from 'react-native';
+
+import decorateMapComponent, {
+ USES_DEFAULT_IMPLEMENTATION,
+ SUPPORTED,
+} from './decorateMapComponent';
+
+const viewConfig = {
+ uiViewClassName: 'AIRCanvasUrlTile',
+};
+
+const propTypes = {
+ ...View.propTypes,
+
+ /**
+ * The url template of the tile server. The patterns {x} {y} {z} will be replaced at runtime
+ * For example, http://c.tile.openstreetmap.org/{z}/{x}/{y}.png
+ */
+ 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,
+ /**
+ * Flag to use the offline tiles instead of the url version
+ */
+ isConnected: PropTypes.bool,
+ /**
+ * Area id to get the tiles from the corrent folder
+ */
+ areaId: PropTypes.string,
+ /**
+ * Alert type supported umd_as_it_happens OR viirs
+ */
+ alertType: PropTypes.string,
+ /**
+ * Min date to get tiles
+ */
+ minDate: PropTypes.string,
+ /**
+ * Max date to get tiles
+ */
+ maxDate: PropTypes.string,
+ /**
+ * Max zoom when the tiles have data
+ */
+ maxZoom: PropTypes.number,
+};
+
+const defaultProps = {
+ maxZoom: 12,
+ isConnected: true,
+};
+
+class CanvasUrlTile extends React.Component {
+ render() {
+ const AIRMapCanvasUrlTile = this.getAirComponent();
+ return (
+
+ );
+ }
+}
+
+CanvasUrlTile.viewConfig = viewConfig;
+CanvasUrlTile.propTypes = propTypes;
+CanvasUrlTile.defaultProps = defaultProps;
+
+module.exports = decorateMapComponent(CanvasUrlTile, {
+ componentType: 'CanvasUrlTile',
+ providers: {
+ google: {
+ ios: SUPPORTED,
+ android: USES_DEFAULT_IMPLEMENTATION,
+ },
+ },
+});
diff --git a/lib/components/MapView.js b/lib/components/MapView.js
index 5eb2284a5..1b416687b 100644
--- a/lib/components/MapView.js
+++ b/lib/components/MapView.js
@@ -15,6 +15,8 @@ import MapPolygon from './MapPolygon';
import MapCircle from './MapCircle';
import MapCallout from './MapCallout';
import MapUrlTile from './MapUrlTile';
+import MapCanvasUrlTile from './MapCanvasUrlTile';
+import MapCanvasInteractionUrlTile from './MapCanvasInteractionUrlTile';
import AnimatedRegion from './AnimatedRegion';
import {
contextTypes as childContextTypes,
@@ -680,6 +682,8 @@ MapView.Polyline = MapPolyline;
MapView.Polygon = MapPolygon;
MapView.Circle = MapCircle;
MapView.UrlTile = MapUrlTile;
+MapView.CanvasUrlTile = MapCanvasUrlTile;
+MapView.CanvasInteractionUrlTile = MapCanvasInteractionUrlTile;
MapView.Callout = MapCallout;
Object.assign(MapView, ProviderConstants);
MapView.ProviderPropType = PropTypes.oneOf(Object.values(ProviderConstants));
diff --git a/package.json b/package.json
index 30ffc43e8..772551c11 100644
--- a/package.json
+++ b/package.json
@@ -55,5 +55,9 @@
"android": {
"sourceDir": "./lib/android"
}
+ },
+ "dependencies": {
+ "@mapbox/geo-viewport": "^0.2.2",
+ "@mapbox/tilebelt": "Vizzuality/tilebelt"
}
}