Zoom to specified markers react-native-maps - google-maps

There is a section in the react-native-maps docs for zooming to an array of markers, however there are no code examples on how to do this either in the docs or in the examples folder (from what I can find)
Can anyone provide an example of how to do this?

In the MapView component docs, there are a few methods: fitToElements, fitToSuppliedMarkers and fitToCoordinates. https://github.com/airbnb/react-native-maps/blob/master/docs/mapview.md#methods
If you want to zoom the map in on some collection of markers on load, you can use componentDidMount to zoom in after the initial render:
class SomeView extends Component {
constructor() {
this.mapRef = null;
}
componentDidMount() {
this.mapRef.fitToSuppliedMarkers(
someArrayOfMarkers,
false, // not animated
);
}
render() {
<MapView
ref={(ref) => { this.mapRef = ref }}
>
{ someArrayOfMarkers }
</MapView>
}
}

Related

Displaying MapView markers from JSON API

I am using React Native map view (https://github.com/react-native-community/react-native-maps) and have successfully got my map to display with manual markers. However, when I try to display my fetched JSON data markers I get constant syntax errors.
import MapView from 'react-native-maps'
import { Marker } from 'react-native-maps';
constructor(props) {
super(props);
this.state = {
myMarkers: []
};
}
componentDidMount() {
this.getMarkers();
}
getMarkers() {
axios
.get("MYAPISOURCE")
.then(response => {
this.setState({
myMarkers: response.data.responser
});
});
}
render() {
return (
<MapView style={styles.map}
rotateEnabled={false}
initialRegion={{
latitude: 37.78825,
longitude: -122.4324,
latitudeDelta: 0.04,
longitudeDelta: 0.05,
}}
>
{this.state.myMarkers.map((post, index)=> {
<MapView.Marker
key={post.ID}
coordinate={{
latitude:{post.lat},
longitude:{post.lng}}}
/>
})}
</MapView>
);
}
My Map View code which keeps getting syntax error: Unexpected tokens, expected "," in the coordinate function. I have tried so many different options but I cannot get the markers and map to work when I have my marker code.
HOWEVER If I run this code below outside of MapView, it successfully displays my data so my API call and myMarkers state is working and has the proper data:
{this.state.myMarkers.map((post, index)=> {
return (
<View key={index}>
<Text>
{post.lat},
{post.lat}
</Text>
</View>
)
})}
This displays the following:
37.78825, -122.4324
33.78825, -121.4324
31.78825, -124.4324
The data from the service is coming after the rendering(after the components render with empty values) so you need to use promise with state to render it once its ready.
examble:-
constructor(){
super(props);
this.state={dataIsHere:false};
}
dataSource()=>{
axios.<><><>.then((data)=>this.setState({dataIsHere:true}));
}
render(){
return(
...
//It will render markers once the data is ready
{this.state.dataIsHere?
this.state.myMarkers.map((post, index)=> {
<MapView.Marker
key={post.ID}
coordinate={{
latitude:{post.lat},
longitude:{post.lng}}}
/>
}):null}
...
)
}

Simulating a click on a React Element

I'm using React with Leaflet, and want to launch the drawing menu immediately upon the component mounting, without making the user click any buttons. The React Leaflet Draw API is a bit opaque on this, and what I'd like to do to make this simple is to trigger a click on the appropriate button programmatically, without the user having to. I'll then hide the button.
The trouble is that I'm not having any luck either using the .click() or the MouseEvent('click') APIs. Here's my attempt at the latter:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../../actions';
import { Polygon, FeatureGroup } from 'react-leaflet';
import { EditControl } from 'react-leaflet-draw';
export class DrawNewPlot extends Component {
componentDidMount() {
let simulateClick = elem => {
let evt = new MouseEvent('click', {
bubbles: true,
view: window
});
};
let drawControl = document.getElementsByClassName('leaflet-draw-toolbar');
simulateClick(drawControl);
}
render() {
return (
<FeatureGroup>
<EditControl
position="bottomright"
onEdited={e => {
e.layers.eachLayer(a => {
this.props.setNewPlotGeojson(a.toGeoJSON());
});
}}
onCreated={e => {
this.props.setNewPlotGeojson(e.layer.toGeoJSON());
}}
draw={{
marker: false,
circle: false,
rectangle: false,
polygon: true,
polyline: false,
circlemarker: false,
edit: false
}}
edit={{ edit: false }}
/>
</FeatureGroup>
);
}
}
function mapStateToProps(state) {
return {
addingNewPlotDetails: state.plots.addingNewPlotDetails
};
}
export default connect(mapStateToProps, actions)(DrawNewPlot);
Any thoughts as to what I'm doing wrong?
Your simulateClick method creates the event, but never dispatches it. Try adding elem.dispatchEvent(evt);
Although I must add that simulating mouse click this way just to trigger some initial side effect feels wrong. I am not familiar with Leaflet, but it could be worth checking if they have some API to set initial state

How to customize/style MapView in Expo on IOS and Android?

I have generated a style on https://mapstyle.withgoogle.com, which gives me a JSON.
I followed the information here: https://github.com/airbnb/react-native-maps
For some reason, the styling does not work.
On IOS, by default, it is not google maps what opens. Solution: you need to add the following property to MapView: provider = { MapView.PROVIDER_GOOGLE }
On Android, it should work right away.
Here is a working example:
import React from "react";
import { StyleSheet, Text } from "react-native";
import { MapView, Constants } from 'expo';
export default class MapScreen extends React.Component {
render() {
return (
<MapView
style = { styles.container }
provider = { MapView.PROVIDER_GOOGLE }
customMapStyle = { generatedMapStyle }
/>
);}
}
const styles = StyleSheet.create({
container: {
flex: 1,
}
});
const generatedMapStyle = [...] // just paste the JSON here.
Also, make sure you have enabled the Maps SDK for iOS/Android services in your Google Cloud Platform, as well as including your API key in app.json. It took me 4 builds to find out....

how do you render GoogleMaps API to a route-specific HTML Div element?

I'm using React Meteor 1.3 and this google maps package.
https://github.com/dburles/meteor-google-maps-react-example.
I can successfully render the map to a single page App, however as soon as I add routing to the mix - things stop working. Specifically when I move the element from an index html template to a JSX component that renders to the page - it breaks. I'm at a bit of a loss here and as the problem is quite vague (in my mind at least) I can't find an answer on google.
What's happening here? Does anyone have an example of this package working with flowrouter?
My current working set up looks like this.
Map.jsx
import React from 'react';
import ReactDOM from 'react-dom';
MyTestMap = React.createClass({
mixins: [ReactMeteorData],
componentDidMount() {
GoogleMaps.load();
},
getMeteorData() {
return {
loaded: GoogleMaps.loaded(),
mapOptions: GoogleMaps.loaded() && this._mapOptions()
};
},
_mapOptions() {
return {
center: new google.maps.LatLng(-37.8136, 144.9631),
zoom: 8
};
},
render() {
if (this.data.loaded)
return <GoogleMap name="mymap" options={this.data.mapOptions} />;
return <div>Loading map...</div>;
}
});
GoogleMap = React.createClass({
propTypes: {
name: React.PropTypes.string.isRequired,
options: React.PropTypes.object.isRequired
},
componentDidMount() {
GoogleMaps.create({
name: this.props.name,
element: ReactDOM.findDOMNode(this),
options: this.props.options
});
GoogleMaps.ready(this.props.name, function(map) {
var marker = new google.maps.Marker({
position: map.options.center,
map: map.instance
});
});
},
componentWillUnmount() {
if (GoogleMaps.maps[this.props.name]) {
google.maps.event.clearInstanceListeners(GoogleMaps.maps[this.props.name].instance);
delete GoogleMaps.maps[this.props.name];
}
},
render() {
return (
<div id="mapId" className="map-container"></div>
)
}
});
if (Meteor.isClient) {
Meteor.startup(function() {
return ReactDOM.render(<MyTestMap />, document.getElementById('root'));
});
}
And then I render this to an Index.html file - however this means that the map is on every page.
<head>
<title>googlemaps-react</title>
</head>
<body>
<div id="root"></div>
</body>
Thanks

How to update another tab`s state

I have two tabs in side a TabBarIOS.
First tab is offer function to add new item into AsyncStorage
Second tab display all item from AsyncStorage
But when i run my app, after adding a new item from First Tab, navigate to second tab i don`t see page re-render, i will need to do a Command+R then i see my new data.
One way to resolve the issue is to read asyncStorage in shouldComponentUpdate, but notice that shouldComponentUpdate will be called constantly by react. While i want to only force update the UI on demand.
So in react native, what is the right way to update state of another UI component?
sample app:
https://drive.google.com/file/d/0B8kAIsj2xDnUMklIQmc0b3NiSVE/view?usp=sharing
Here's what I'm talking about. You will probably want to refactor this a bit once done, but in general here's how I think this would look in the end. I've still a lot to learn about using the lifecycle functions properly, and I'm not sure you'll need them now that I think about it.
appContainer.js
I've essentially removed your NavigatorIOS. I think those were a mistake. Instead, I'm replacing them with the components, passing down the data as props, including a callback function for the button press. I've moved the addData function up a level.
class AppContainer extends Component {
constructor(props) {
super(props);
this.state = {
selectedTab: 'data-list',
dataLen: 0
}
AsyncStorage.getItem("all-data")
.then((data)=>{
if(!data)
{
data = "[]";
}
var dataObj = JSON.parse(data);
this.setState({
dataLen : dataObj.length
});
});
this.addData.bind(this);
}
addData() {
AsyncStorage.getItem("all-data")
.then((data)=>{
if(!data)
{
data = "[]";
}
var dataObj = JSON.parse(data);
dataObj.push({
val: Date.now()
});
AsyncStorage.setItem("all-data", JSON.stringify(dataObj));
this.setState({
dataLen: dataObj.length
});
});
}
render() {
return (
<TabBarIOS style={styles.container}>
<TabBarIOS.Item
title="Add Data"
selected={this.state.selectedTab == 'add-data'}
onPress={()=> this.setState({selectedTab: 'add-data'})}
>
<AddData onButtonPress={this.addData} dataLen={this.state.dataLen} />
</TabBarIOS.Item>
<TabBarIOS.Item
title="Data List"
selected={this.state.selectedTab == 'data-list'}
onPress={()=> this.setState({selectedTab: 'data-list'})}
badge={this.state.dataLen}
>
<DataList dataLen={this.state.dataLen} />
</TabBarIOS.Item>
</TabBarIOS>
);
}
}
addData.js
This will simplify your sub-components significantly...
class AddData extends Component {
constructor(props) {
super(props);
}
render() {
return (
<View style={styles.container}>
<Text>{this.props.dataLen}</Text>
<TouchableHighlight
onPress={this.props.onButtonPress}
style={styles.button}>
<Text style={styles.buttonText}>Plus One</Text>
</TouchableHighlight>
</View>
);
}
}
dataList.js
class PlayerList extends Component {
constructor(props) {
super(props);
}
render() {
return (
<View style={styles.container}>
<Text>{this.props.dataLen}</Text>
</View>
);
}
}
Use Redux
Have you ever thought about using Redux or something equally?
I had the same problem. Eventually the use of Redux solved it for me.
Official documentation
I highly recommend you the official documentation of Redux.
Example
I will not try to teach you Redux, since the documentation is already really good. But I will try to outline some parts, to give you an idea how to use Redux. I really can't update your existing app, since it's not a trivial tweak. It wouldn't be really sufficient, if the most changes would be cryptic for you.
In your action creators you need to call AsyncStorage and use the values in your reducer to update your state. The important part is the connection of your components with the Redux store. You have to connect it with the component, which you load via NavigatorIOS, since NavigatorIOS doesn't update when passProps update.
<NavigatorIOS
initialRoute={{
component: MyView,
title: 'My View Title',
}}
/>
// MyView Component
...
class MyView extends Component {
...
}
// Prepare the information of the global state and only pass the relevant values as `props` to the component.
function mapStateToProps(state) {
const { isFetching, isError, lastUpdated } = state.posts;
const { entities: posts } = state.posts || { entities: {} };
return {
posts,
isFetching,
isError,
lastUpdated
}
}
export default connect(mapStateToProps)(MyView);