I'm using react native maps, redux, redux thunk (and a lot of other cool stuffs)
Any ideas about how to animate my marker and my polyline like this
map animation on dribbble
I already have the markers and the polyline on my map and I know how to move the map. I want to add 3 effects :
the pulse effect on the marker,
draw the polyline from start to arrival with a fancy animation
make a gradient color on the polyline
Thanks everyone !
It does not appear that you can animate the drawing of a polyline in react native maps, but you can give it a gradient color with the following code (iOS only):
import MapView, { Polyline } from 'react-native-maps';
<MapView>
<Polyline
coordinates={[
{ latitude: 37.8025259, longitude: -122.4351431 },
{ latitude: 37.7896386, longitude: -122.421646 },
{ latitude: 37.7665248, longitude: -122.4161628 },
{ latitude: 37.7734153, longitude: -122.4577787 },
{ latitude: 37.7948605, longitude: -122.4596065 },
{ latitude: 37.8025259, longitude: -122.4351431 }
]}
strokeColor="#000" // fallback for when `strokeColors` is not supported by the map-provider
strokeColors={[
'#7F0000',
'#00000000', // no color, creates a "long" gradient between the previous and next coordinate
'#B24112',
'#E5845C',
'#238C23',
'#7F0000'
]}
strokeWidth={6}
/>
Here is an example of an animated Marker as well:
import Mapview, { AnimatedRegion, Marker } from 'react-native-maps';
getInitialState() {
return {
coordinate: new AnimatedRegion({
latitude: LATITUDE,
longitude: LONGITUDE,
}),
};
}
componentWillReceiveProps(nextProps) {
const duration = 500
if (this.props.coordinate !== nextProps.coordinate) {
if (Platform.OS === 'android') {
if (this.marker) {
this.marker._component.animateMarkerToCoordinate(
nextProps.coordinate,
duration
);
}
} else {
this.state.coordinate.timing({
...nextProps.coordinate,
duration
}).start();
}
}
}
render() {
return (
<MapView initialRegion={...}>
<MapView.Marker.Animated
ref={marker => { this.marker = marker }}
coordinate={this.state.coordinate}
/>
</MapView>
);
}
More examples can be seen here: https://github.com/react-community/react-native-maps
Related
This is the first time I'm working with maps in React Native. I've some initial values setup like:
const [latitude, setLatitude] = useState(38.889815);
const [longitude, setLongitude] = useState(-77.005900);
const [heading, setHeading] = useState(0);
const GEOLOCATION_OPTIONS = {
pitch: 65,
altitude: 100,
zoom: 17,
};
const [camera, setCamera] = useState({
center: {
latitude: latitude,
longitude: longitude
},
heading: heading,
pitch: GEOLOCATION_OPTIONS.pitch,
zoom: GEOLOCATION_OPTIONS.zoom,
altitude: GEOLOCATION_OPTIONS.altitude
});
Then I'm asking for user location
function getCurrentLocation() {
Geolocation.watchPosition(
position => {
const myLastPosition = myPosition;
const myPosition = position.coords;
if (myPosition != myLastPosition) {
setLatitude(myPosition.latitude)
setLongitude(myPosition.longitude)
setHeading(myPosition.heading)
setCamera(
{
center: {
latitude: myPosition.latitude,
longitude: myPosition.longitude
},
heading: heading,
pitch: GEOLOCATION_OPTIONS.pitch,
zoom: GEOLOCATION_OPTIONS.zoom,
altitude: GEOLOCATION_OPTIONS.altitude
}
)
}
},
null,
{
enableHighAccuracy: true,
}
);
};
useEffect(() => {
getCurrentLocation()
}, [camera])
And finally, I'm displaying the map as
<View style={{ flex: 1 }}>
<MapView
ref={(mapView) => { _mapView = mapView }}
provider={PROVIDER_GOOGLE}
scrollEnabled={false}
pitchEnabled={false}
rotateEnabled={false}
loadingEnabled={true}
moveOnMarkerPress={true}
showsUserLocation={true}
showsBuildings={false}
showsCompass={true}
showsMyLocationButton={false}
followsUserLocation={true}
style={styles.mapMain}
userLocationPriority={'high'}
minZoomLevel={GEOLOCATION_OPTIONS.zoom}
maxZoomLevel={GEOLOCATION_OPTIONS.zoom}
initialCamera={camera}>
</MapView>
</View >
The options which are true and false are basically there to stop the user from moving the app. The idea is that the map will update as per user location and will also rotate if the user heading is updated.
Now when I manually change the user location for the iOS simulator I'm running I do Features > Location > Custom Location and doing so I can see that the user location marker is moved to that position but the camera is not getting updated through the values of the state are also updated.
Also, the map doesn't rotate if the heading is updated.
What I'm trying to do is the same as navigation, it moves as the user moves and rotates when the user takes a turn.
i'm currently working on a project using google maps api with react native, but i'm struggling to handle get current user location with redux, this is my action ,how can i fix it :
import MAPSActionTypes from "./mapsactiontypes";
export const currentlocation = () => {
return navigator.geolocation.getCurrentPosition(
(position) => {
console.log("wokeeey");
console.log(position);
{payload: position}
},
(error) => console.log(error)),
{ enableHighAccuracy: false, timeout: 200000, maximumAge: 1000 },
};
I'm not sure what you're error is exactly, but I found an issue here on doing the exact same thing. It referred to a code sample you can find here, which I've reproduced below.
import React from 'react';
import {
Alert,
Platform,
StyleSheet
} from 'react-native';
import MapView from 'react-native-maps'
const LATITUDE_DELTA = 0.01;
const LONGITUDE_DELTA = 0.01;
const initialRegion = {
latitude: -37.78825,
longitude: -122.4324,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}
class MyMapView extends React.Component {
map = null;
state = {
region: {
latitude: -37.78825,
longitude: -122.4324,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
},
ready: true,
filteredMarkers: []
};
setRegion(region) {
if(this.state.ready) {
setTimeout(() => this.map.mapview.animateToRegion(region), 10);
}
//this.setState({ region });
}
componentDidMount() {
console.log('Component did mount');
this.getCurrentPosition();
}
getCurrentPosition() {
try {
navigator.geolocation.getCurrentPosition(
(position) => {
const region = {
latitude: position.coords.latitude,
longitude: position.coords.longitude,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
};
this.setRegion(region);
},
(error) => {
//TODO: better design
switch (error.code) {
case 1:
if (Platform.OS === "ios") {
Alert.alert("", "Para ubicar tu locación habilita permiso para la aplicación en Ajustes - Privacidad - Localización");
} else {
Alert.alert("", "Para ubicar tu locación habilita permiso para la aplicación en Ajustes - Apps - ExampleApp - Localización");
}
break;
default:
Alert.alert("", "Error al detectar tu locación");
}
}
);
} catch(e) {
alert(e.message || "");
}
};
onMapReady = (e) => {
if(!this.state.ready) {
this.setState({ready: true});
}
};
onRegionChange = (region) => {
console.log('onRegionChange', region);
};
onRegionChangeComplete = (region) => {
console.log('onRegionChangeComplete', region);
};
render() {
const { region } = this.state;
const { children, renderMarker, markers } = this.props;
return (
<MapView
showsUserLocation
ref={ map => { this.map = map }}
data={markers}
initialRegion={initialRegion}
renderMarker={renderMarker}
onMapReady={this.onMapReady}
showsMyLocationButton={false}
onRegionChange={this.onRegionChange}
onRegionChangeComplete={this.onRegionChangeComplete}
style={StyleSheet.absoluteFill}
textStyle={{ color: '#bc8b00' }}
containerStyle={{backgroundColor: 'white', borderColor: '#BC8B00'}}>
{markers.map(renderMarker)}
{children && children || null}
</ClusteredMapView>
);
}
}
export default MyMapView;
I have a little problem with rendering markers.
Everything gets slow and laggy.
You can see it on this video: https://streamable.com/fjqe7
Is there a way to render my Markers smoother?
this is what I tried
<View style={styles.slider}>
<Slider
maximumValue={1000}
minimumValue={100}
step={50}
value={this.state.newRadius}
onValueChange={newRadius => {
this.setState({newRadius})
this.getLocation()
}}
/>
<View>
<Text>Radius: {this.state.newRadius} meter</Text>
</View>
</View>
onValueChange I'm loading the getLocation()
my getLocation is for showing and filtering my Markers inside my RadiusValue
getLocation(){
let { region } = this.state;
let { latitude, longitude } = region;
let markers = spielanlagen.map(marker => {
let name = marker.name
let image = marker.image
let street = marker.street
console.log(name);
console.log(marker.coordinate);
let coords = marker.coordinate
return {
coordinate: {
latitude: coords[0],
longitude: coords[1],
},
name: marker.name,
street: marker.street,
image: marker.image
}
}).filter(marker => {
let distance = this.calculateDistance(latitude, longitude, marker.coordinate.latitude, marker.coordinate.longitude);
return distance <= this.state.newRadius;
});
this.setState({
markers: markers,
loaded: true,
});
};
calculateDistance(origLat, origLon, markerLat, markerLon) {
return geolib.getDistance(
{latitude: origLat, longitude: origLon},
{latitude: markerLat, longitude: markerLon}
);
}
I hope there is a better solution with re-rendering the markers.
Hey Guys Iam getting an error on my Android Devices.
On Iphone it works very well I'am getting my Markers in maps but on Android iam getting this Error
Click for the Image
Since i upgraded the Code with geolib where iam filtering markers out which are not near to me it won't work on Android...
Anybody an idea?
this is my Code:
import React from 'react';
import MapView from 'react-native-maps';
import Marker from 'react-native-maps';
import Geolib from 'geolib';
import {
View,
Text,
StyleSheet,
Button,
} from "react-native";
const geolib = require('geolib');
class Grillplaetze extends React.Component {
constructor() {
super();
this.state = {
markers: [],
loaded: false
}
}
componentDidMount() {
this.getPosition();
}
getPosition(){
navigator.geolocation.getCurrentPosition(
(position) => {
console.log(position);
this.setState({
region: {
latitude: position.coords.latitude,
longitude: position.coords.longitude,
latitudeDelta: 0.020,
longitudeDelta: 0.020,
}
}, () => this.getLocations());
},
(error) => this.setState({ error: error.message }),
{ enableHighAccuracy: false, timeout: 200000, maximumAge: 1000 },
);
}
getLocations() {
return fetch('http://media-panda.de/bp/whs.geojson')
.then(response => response.json())
.then(responseData => {
let { region } = this.state;
let { latitude, longitude } = region;
let markers = responseData.features.map(feature => {
let coords = feature.geometry.coordinates
return {
coordinate: {
latitude: coords[1],
longitude: coords[0],
}
}
}).filter(marker => {
let distance = this.calculateDistance(latitude, longitude, marker.coordinate.latitude, marker.coordinate.longitude);
return distance <= 500;
});
this.setState({
markers: markers,
loaded: true,
});
}).done();
}
calculateDistance(origLat, origLon, markerLat, markerLon) {
return geolib.getDistance(
{latitude: origLat, longitude: origLon},
{latitude: markerLat, longitude: markerLon}
);
}
render() {
return (
<View style={styles.container}>
<MapView.Animated
style={styles.map}
region={this.state.region}
showsUserLocation={true}
>
{this.state.markers.map(marker => (
<MapView.Marker
coordinate={marker.coordinate}
/>
))}
<MapView.Circle
key = { (this.state.latitude + this.state.longitude).toString() }
center = { this.state.region }
radius = { 500 }
strokeWidth = { 1 }
strokeColor = { '#1a66ff' }
fillColor = { 'rgba(230,238,255,0.5)' }
/>
</MapView.Animated>
</View>
);
}
}
export default Grillplaetze;
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#ecf0f1',
},
map: {
width: "100%",
height: "100%",
},
})
Your errors aren't related to your implementation of the geolib but instead they are due to your implementation of the MapView.Circle.
If we look at the documentation the MapView.Circle we see the following:
| Prop | Type | Default | Note |
|----------|----------|------------|------------------------------------------------|
| `center` | `LatLng` | (Required) | The coordinate of the center of the circle .
| `radius` | `Number` | (Required) | The radius of the circle to be drawn (in meters)
Both the center and the radius are required fields.
If we look at your code:
<MapView.Circle
key = { (this.state.latitude + this.state.longitude).toString() }
center = { this.state.region }
radius = { 500 }
strokeWidth = { 1 }
strokeColor = { '#1a66ff' }
fillColor = { 'rgba(230,238,255,0.5)' }
/>
It would appear that you have set them, however you have not actually set the region. You can confirm this by checking your initial state.
constructor () {
super();
this.state = {
markers: [],
loaded: false
}
}
Notice that you have not set an initial region for the map. This is what is causing your error. The app is trying to handle the undefined value for the region.
To overcome this the easiest way is to set an initial region for the map in state.
Something like this:
constructor () {
super();
this.state = {
markers: [],
loaded: false,
region: {
latitude: 0,
longitude: 0,
latitudeDelta: 0.020,
longitudeDelta: 0.020
},
latitude: 1,
longitude: 1
};
}
If you app is for a specific region then it may make sense to pick an initial region that is close to where you app is going to be used.
Also note in your MapView.Circle code you are also using undefined values of latitude and longitude for the key. I don't think that you need to define a key property for the MapView.Circle. I cannot find any mention of this being a requirement in the documentation.
Making the above changes allows the code to work.
Some other points.
You are importing geolib twice. You only need to do it once. You should either have import GeoLib from 'geolib'; or const geolib = require('geolib'); you don't need both. Seeing as you are using geolib with a lower case, I would just remove import GeoLib from 'geolib';
You are importing Markers from react-native-maps in the wrong way. It should be imported as import { Markers } from 'react-native-maps, however you are using the Markers as MapView.Markers which is absolutely fin. I think you can remove the unused and incorrect import Markers from 'react-native-maps
MapView.Animated I am not 100% that this is correct or required. I haven't seen it used in this way before. However if it is not causing you issues then I suppose it isn't really a problem.
You should also add a key prop on your Markers so that you suppress the warning that it is missing. This should be something unique.
I'm using Expo and React Native to render a MapView and trying to display some basic Markers, however I can't even get the default ones to display.
Below is the data that is being passed to the global state of sites :
const sampleSiteMarkers = [
{
id: 1,
title: 'Twin Lakes Hidden Spot',
description: 'Beautiful view of Twin Lakes off this hidden forest road.',
coordinate: {
latitude: -106.391015,
longitude: 39.085855
}
},
{
id: 2,
title: 'Lily Lake',
description: 'Nice view of the lilypads in this secluded spot, but a pretty tough road to reach it.',
coordinate: {
latitude: -106.368051,
longitude: 39.351661
}
},
{
id: 3,
title: 'Slide Lake',
description: 'Pretty riverside camping, but a REALLY nasty road to get there.',
coordinate: {
latitude: -106.389204,
longitude: 39.372171
}
}
];
And below is my code to render the actual MapView. Note that the MapView renders fine, but the Markers themselves do not appear.
// 3rd party libraries - core
import React from 'react';
import {View, Text, StyleSheet} from 'react-native';
import {MapView} from 'expo';
const {Marker} = MapView;
// 3rd party libraries - additional
import _ from 'lodash';
const SearchMap = ({mapLoaded, lastKnownRegion, updateRegion, sites}) => {
const {fillScreen} = styles;
const renderSites = () => {
// sites I showed at the top of this issue come in fine from props
const renderedSites = _.map(sites, site => {
const {title, description, coordinate, id} = site;
return (
<Marker
key={id}
title={title}
description={description}
coordinate={coordinate}
/>
);
});
// if I inspect renderedSites, I see the Marker element, but it doesn't render
return renderedSites;
};
const renderMap = () => {
return (
<MapView
style={fillScreen}
initialRegion={lastKnownRegion}
onRegionChangeComplete={updateRegion}
rotateEnabled={false}
>
{renderSites()}
</MapView>
)
};
return (
<View style={fillScreen}>
{renderMap()}
</View>
);
};
const styles = StyleSheet.create({
fillScreen: {
flex: 1
}
});
I've looked at the docs and other StackOverflow posts, and I can't figure out why it won't render.
Am I missing something obvious? Thanks in advance.
Welp. This is embarrassing.
Turns out I did miss something obvious.
I had the longitude and latitude values flipped. Wow.
Here's how it SHOULD have looked, and it now renders fine.
const sampleSiteMarkers = [
{
id: 1,
title: 'Twin Lakes Hidden Spot',
description: 'Beautiful view of Twin Lakes off this hidden forest road.',
coordinate: {
longitude: -106.391015,
latitude: 39.085855
}
},
{
id: 2,
title: 'Lily Lake',
description: 'Nice view of the lilypads in this secluded spot, but a pretty tough road to reach it.',
coordinate: {
longitude: -106.368051,
latitude: 39.351661
}
},
{
id: 3,
title: 'Slide Lake',
description: 'Pretty riverside camping, but a REALLY nasty road to get there.',
coordinate: {
longitude: -106.389204,
latitude: 39.372171
}
}
];
Super frustrating waste of 5+ hours, and I was sure that I had checked that. But, hey, if anyone else runs into this... double, TRIPLE check that you're plugging in the right lat/lng.