-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added MBTiles support for iOS and Android (#2208)
* Added MBTiles support for iOS and Android * Added changes regarding the comments of @h3ll0w0rld123 from here: #2208 (comment) * Added whitespaces * Hotfix: Imported exceptions. Changed database.close() * Hotfix: Removed the finally statemend. Resulted in always returning null * Removed repetition of returns. Moved everything into finally statement instead * Throwing exceptions * Added MapView.MbTile to Readme. Inclduded the component in the Readme * Added example file * Included more information in Readme * Edited example file accodring to linter errors in Pull Request * Edited index.d.ts according to merge conflicts in Pull Request. Had to change a few lines * Edited example file according to linter errors in merge request * Edited example file according to linter errors in merge request. I am starting not to like Travis... * Edited example file according to linter errors in merge request. I am starting not to like Travis...
- Loading branch information
Showing
30 changed files
with
6,224 additions
and
270 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import React, { Component } from 'react'; | ||
import { | ||
StyleSheet, | ||
View, | ||
Dimensions, | ||
TouchableOpacity, | ||
Text, | ||
Platform, | ||
} from 'react-native'; | ||
import MapView from 'react-native-maps'; | ||
|
||
const { width, height } = Dimensions.get('window'); | ||
|
||
const ASPECT_RATIO = width / height; | ||
const LATITUDE = 23.736906; | ||
const LONGITUDE = 90.397768; | ||
const LATITUDE_DELTA = 0.022; | ||
const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; | ||
|
||
|
||
type Props = {}; | ||
export default class App extends Component<Props> { | ||
constructor(props) { | ||
super(props); | ||
|
||
this.state = { | ||
offlineMap: false, | ||
}; | ||
} | ||
|
||
_toggleOfflineMap = () => { | ||
this.setState({ | ||
offlineMap: !this.state.offlineMap, | ||
}); | ||
} | ||
|
||
render() { | ||
return ( | ||
<View | ||
style={styles.container} | ||
> | ||
<MapView | ||
style={styles.map} | ||
initialRegion={{ | ||
latitude: LATITUDE, | ||
longitude: LONGITUDE, | ||
latitudeDelta: LATITUDE_DELTA, | ||
longitudeDelta: LONGITUDE_DELTA, | ||
}} | ||
loadingEnabled | ||
loadingIndicatorColor="#666666" | ||
loadingBackgroundColor="#eeeeee" | ||
mapType={Platform.OS === 'android' && this.state.offlineMap ? 'none' : 'standard'} | ||
> | ||
{this.state.offlineMap ? | ||
<MapView.MbTile | ||
pathTemplate={'Path/to/mBTilesDatabase.mbtiles'} | ||
tileSize={256} | ||
/> : null} | ||
</MapView> | ||
<TouchableOpacity | ||
style={styles.button} | ||
onPress={() => this._toggleOfflineMap()} | ||
> | ||
<Text> {this.state.offlineMap ? 'Switch to Online Map' : 'Switch to Offline Map'} </Text> | ||
</TouchableOpacity> | ||
</View> | ||
); | ||
} | ||
} | ||
|
||
const styles = StyleSheet.create({ | ||
container: { | ||
...StyleSheet.absoluteFillObject, | ||
alignItems: 'center', | ||
}, | ||
button: { | ||
position: 'absolute', | ||
bottom: 20, | ||
backgroundColor: 'lightblue', | ||
zIndex: 999999, | ||
height: 50, | ||
width: width / 2, | ||
borderRadius: width / 2, | ||
justifyContent: 'center', | ||
alignItems: 'center', | ||
}, | ||
map: { | ||
...StyleSheet.absoluteFillObject, | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,17 @@ | ||
import MapView, { Animated, MAP_TYPES, ProviderPropType } from './lib/components/MapView'; | ||
import Marker from './lib/components/MapMarker.js'; | ||
import Overlay from './lib/components/MapOverlay.js'; | ||
import MapView from './lib/components/MapView'; | ||
|
||
export { default as Marker } from './lib/components/MapMarker.js'; | ||
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 LocalTile } from './lib/components/MapLocalTile.js'; | ||
export { default as MbTile } from './lib/components/MapMbTile.js'; | ||
export { default as Overlay } from './lib/components/MapOverlay.js'; | ||
export { default as Callout } from './lib/components/MapCallout.js'; | ||
export { default as AnimatedRegion } from './lib/components/AnimatedRegion.js'; | ||
|
||
export { Marker, Overlay }; | ||
export { Animated, MAP_TYPES, ProviderPropType }; | ||
|
||
export { Animated, ProviderPropType, MAP_TYPES } from './lib/components/MapView.js'; | ||
export const PROVIDER_GOOGLE = MapView.PROVIDER_GOOGLE; | ||
export const PROVIDER_DEFAULT = MapView.PROVIDER_DEFAULT; | ||
|
||
export const MarkerAnimated = Marker.Animated; | ||
export const OverlayAnimated = Overlay.Animated; | ||
|
||
export default MapView; | ||
|
150 changes: 150 additions & 0 deletions
150
lib/android/src/main/java/com/airbnb/android/react/maps/AirMapMbTile.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
package com.airbnb.android.react.maps; | ||
|
||
import android.content.Context; | ||
|
||
import com.google.android.gms.maps.GoogleMap; | ||
import com.google.android.gms.maps.model.Tile; | ||
import com.google.android.gms.maps.model.TileOverlay; | ||
import com.google.android.gms.maps.model.TileOverlayOptions; | ||
import com.google.android.gms.maps.model.TileProvider; | ||
|
||
import java.io.File; | ||
|
||
import android.os.Environment; | ||
import android.database.sqlite.SQLiteDatabase; | ||
import android.database.sqlite.SQLiteCantOpenDatabaseException; | ||
import android.database.sqlite.SQLiteDatabaseCorruptException; | ||
import android.database.sqlite.SQLiteDatabaseLockedException; | ||
import android.database.Cursor; | ||
|
||
/** | ||
* Created by Christoph Lambio on 30/03/2018. | ||
* Based on AirMapLocalTileManager.java | ||
* Copyright (c) zavadpe | ||
*/ | ||
|
||
public class AirMapMbTile extends AirMapFeature { | ||
|
||
class AIRMapMbTileProvider implements TileProvider { | ||
private static final int BUFFER_SIZE = 16 * 1024; | ||
private int tileSize; | ||
private String pathTemplate; | ||
|
||
|
||
public AIRMapMbTileProvider(int tileSizet, String pathTemplate) { | ||
this.tileSize = tileSizet; | ||
this.pathTemplate = pathTemplate; | ||
} | ||
|
||
@Override | ||
public Tile getTile(int x, int y, int zoom) { | ||
byte[] image = readTileImage(x, y, zoom); | ||
return image == null ? TileProvider.NO_TILE : new Tile(this.tileSize, this.tileSize, image); | ||
} | ||
|
||
public void setPathTemplate(String pathTemplate) { | ||
this.pathTemplate = pathTemplate; | ||
} | ||
|
||
public void setTileSize(int tileSize) { | ||
this.tileSize = tileSize; | ||
} | ||
|
||
private byte[] readTileImage(int x, int y, int zoom) { | ||
String rawQuery = "SELECT * FROM map INNER JOIN images ON map.tile_id = images.tile_id WHERE map.zoom_level = {z} AND map.tile_column = {x} AND map.tile_row = {y}"; | ||
byte[] tile = null; | ||
try { | ||
SQLiteDatabase offlineDataDatabase = SQLiteDatabase.openDatabase(this.pathTemplate, null, SQLiteDatabase.OPEN_READONLY); | ||
String query = rawQuery.replace("{x}", Integer.toString(x)) | ||
.replace("{y}", Integer.toString(y)) | ||
.replace("{z}", Integer.toString(zoom)); | ||
Cursor cursor = offlineDataDatabase.rawQuery(query, null); | ||
if (cursor.moveToFirst()) { | ||
tile = cursor.getBlob(5); | ||
} | ||
cursor.close(); | ||
offlineDataDatabase.close(); | ||
} catch (SQLiteCantOpenDatabaseException e) { | ||
e.printStackTrace(); | ||
throw e; | ||
} catch (SQLiteDatabaseCorruptException e) { | ||
e.printStackTrace(); | ||
throw e; | ||
} catch (SQLiteDatabaseLockedException e) { | ||
e.printStackTrace(); | ||
throw e; | ||
} catch (Exception e) { | ||
e.printStackTrace(); | ||
throw e; | ||
} finally { | ||
return tile; | ||
} | ||
} | ||
} | ||
|
||
private TileOverlayOptions tileOverlayOptions; | ||
private TileOverlay tileOverlay; | ||
private AirMapMbTile.AIRMapMbTileProvider tileProvider; | ||
|
||
private String pathTemplate; | ||
private float tileSize; | ||
private float zIndex; | ||
|
||
public AirMapMbTile(Context context) { | ||
super(context); | ||
} | ||
|
||
public void setPathTemplate(String pathTemplate) { | ||
this.pathTemplate = pathTemplate; | ||
if (tileProvider != null) { | ||
tileProvider.setPathTemplate(pathTemplate); | ||
} | ||
if (tileOverlay != null) { | ||
tileOverlay.clearTileCache(); | ||
} | ||
} | ||
|
||
public void setZIndex(float zIndex) { | ||
this.zIndex = zIndex; | ||
if (tileOverlay != null) { | ||
tileOverlay.setZIndex(zIndex); | ||
} | ||
} | ||
|
||
public void setTileSize(float tileSize) { | ||
this.tileSize = tileSize; | ||
if (tileProvider != null) { | ||
tileProvider.setTileSize((int)tileSize); | ||
} | ||
} | ||
|
||
public TileOverlayOptions getTileOverlayOptions() { | ||
if (tileOverlayOptions == null) { | ||
tileOverlayOptions = createTileOverlayOptions(); | ||
} | ||
return tileOverlayOptions; | ||
} | ||
|
||
private TileOverlayOptions createTileOverlayOptions() { | ||
TileOverlayOptions options = new TileOverlayOptions(); | ||
options.zIndex(zIndex); | ||
this.tileProvider = new AirMapMbTile.AIRMapMbTileProvider((int)this.tileSize, this.pathTemplate); | ||
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(); | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
lib/android/src/main/java/com/airbnb/android/react/maps/AirMapMbTileManager.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
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; | ||
|
||
/** | ||
* Created by Christoph Lambio on 30/03/2018. | ||
* Based on AirMapLocalTileManager.java | ||
* Copyright (c) zavadpe | ||
*/ | ||
public class AirMapMbTileManager extends ViewGroupManager<AirMapMbTile> { | ||
private DisplayMetrics metrics; | ||
|
||
public AirMapMbTileManager(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 "AIRMapMbTile"; | ||
} | ||
|
||
@Override | ||
public AirMapMbTile createViewInstance(ThemedReactContext context) { | ||
return new AirMapMbTile(context); | ||
} | ||
|
||
@ReactProp(name = "pathTemplate") | ||
public void setPathTemplate(AirMapMbTile view, String pathTemplate) { view.setPathTemplate(pathTemplate); } | ||
|
||
@ReactProp(name = "tileSize", defaultFloat = 256f) | ||
public void setTileSize(AirMapMbTile view, float tileSize) { | ||
view.setTileSize(tileSize); | ||
} | ||
|
||
@ReactProp(name = "zIndex", defaultFloat = -1.0f) | ||
public void setZIndex(AirMapMbTile view, float zIndex) { | ||
view.setZIndex(zIndex); | ||
} | ||
|
||
} |
Oops, something went wrong.