How to draw polygon on react native maps with many points(latitude and longitude)?

17,446

Solution 1

You have to map through your response and then transform your coordinates into the type given in the documentation

type LatLng {
  latitude: Number,
  longitude: Number,
}

Assuming your API response is saved in apiResponse, try this. You access your object with keys and array Positions and the you map through your coordinates array.

const polygon = apiResponse.data.routeGeoJSON[0].geometry.coordinates[0].map(coordsArr => { 
    let coords = {
        latitude: coordsArr[1],
        longitude: coordsArr[0],
      }
      return coords;
});

The const polygon is what you give your

<MapView.Polygon
    coordinates={polygon} />

Solution 2

Here is the final code:

Just create a file Maps.js and paste code and call it from the navigator

import React, { Component } from 'react'
import {
  StyleSheet,
  View,
  Text,
  Dimensions,
  TouchableOpacity
} from 'react-native'

import MapView, {
  MAP_TYPES,
  Polygon,
  ProviderPropType,
  PROVIDER_GOOGLE
} 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
let id = 0

class Maps extends Component {
  constructor(props) {
    super(props)
    this.state = {
      region: {
        latitude: LATITUDE,
        longitude: LONGITUDE,
        latitudeDelta: LATITUDE_DELTA,
        longitudeDelta: LONGITUDE_DELTA
      },
      polygons: [],
      editing: null,
      creatingHole: false
    }
  }

  finish() {
   const { polygons, editing } = this.state;
   this.setState({
   polygons: [...polygons, editing],
   editing: null,
   creatingHole: false,
  });
  }

  clear = () => {
    this.setState({
      polygons: [],
      editing: null,
      creatingHole: false
    })
  }

  createHole() {
    const { editing, creatingHole } = this.state
    if (!creatingHole) {
      this.setState({
        creatingHole: true,
        editing: {
          ...editing,
          holes: [...editing.holes, []]
        }
      })
    } else {
      const holes = [...editing.holes]
      if (holes[holes.length - 1].length === 0) {
        holes.pop()
        this.setState({
          editing: {
            ...editing,
            holes
          }
        })
      }
      this.setState({ creatingHole: false })
    }
  }

  onPress(e) {
    console.log(this.state.polygons)
    const { editing, creatingHole } = this.state
    if (!editing) {
      this.setState({
        editing: {
          id: id++,
          coordinates: [e.nativeEvent.coordinate],
          holes: []
        }
      })
    } else if (!creatingHole) {
      this.setState({
        editing: {
          ...editing,
          coordinates: [...editing.coordinates, e.nativeEvent.coordinate]
        }
      })
    } else {
      const holes = [...editing.holes]
      holes[holes.length - 1] = [
        ...holes[holes.length - 1],
        e.nativeEvent.coordinate
      ]
      this.setState({
        editing: {
          ...editing,
          id: id++, // keep incrementing id to trigger display refresh
          coordinates: [...editing.coordinates],
          holes
        }
      })
    }
  }

  render() {
    const mapOptions = {
      scrollEnabled: true
    }

    if (this.state.editing) {
      mapOptions.scrollEnabled = false
      mapOptions.onPanDrag = e => this.onPress(e)
    }

    return (
      <View style={styles.container}>
        <MapView
          provider={PROVIDER_GOOGLE}
          style={styles.map}
          mapType={MAP_TYPES.SATELLITE}
          initialRegion={this.state.region}
          onPress={e => this.onPress(e)}
          {...mapOptions}
        >
          {this.state.polygons.map(polygon => (
            <Polygon
              key={polygon.id}
              coordinates={polygon.coordinates}
              holes={polygon.holes}
              strokeColor="#F00"
              fillColor="rgba(255,0,0,0.5)"
              strokeWidth={1}
            />
          ))}
          {this.state.editing && (
            <Polygon
              key={this.state.editing.id}
              coordinates={this.state.editing.coordinates}
              holes={this.state.editing.holes}
              strokeColor="#000"
              fillColor="rgba(255,0,0,0.5)"
              strokeWidth={1}
            />
          )}
        </MapView>

        <View style={styles.buttonContainer}>
          {this.state.editing && (
            <TouchableOpacity
              onPress={() => this.createHole()}
              style={[styles.bubble, styles.button]}
            >
              <Text>
                {this.state.creatingHole ? 'Finish Hole' : 'Create Hole'}
              </Text>
            </TouchableOpacity>
          )}
          {this.state.editing && (
            <TouchableOpacity
              onPress={() => this.finish()}
              style={[styles.bubble, styles.button]}
            >
              <Text>Finish</Text>
            </TouchableOpacity>
          )}
        </View>
        <TouchableOpacity
          onPress={() => this.clear()}
          style={[styles.bubble, styles.button]}
        >
          <Text>Clear</Text>
        </TouchableOpacity>
      </View>
    )
  }
}

Maps.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 Maps

Don't forget to add dependencies to AndroidManifest.xml

In manifest tag:-

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />

In application tag:-

<meta-data
     android:name="com.google.android.geo.API_KEY"
     android:value="Paste your API key from google maps [https://developers.google.com/maps/documentation/embed/get-api-key]"
/>
Share:
17,446
Fang
Author by

Fang

Updated on June 26, 2022

Comments

  • Fang
    Fang almost 2 years

    I have been following this documentation for drawing polygon on map. Now I tried to fetch the value for latitude and longitude from API and draw it. For a small amount of value, it is not a problem as I just directly wrote it from using state.

    Below are my render function

    import React, { Component } from 'react';
    import { View, Text, StyleSheet, Dimensions } from 'react-native';
    import { Card, Button } from 'react-native-elements';
    import { MapView } from 'expo';
    import { connect } from 'react-redux';
    
    const window = Dimensions.get('window');
    
    class PolygonPage extends Component {
        constructor(props) {
            super(props);
    
            this.state = {
                polygon: [
                {
                    latitude: this.props.points.data.routeGeoJSON.features[0].geometry.coordinates[0][0][1],
                    longitude: this.props.points.data.routeGeoJSON.features[0].geometry.coordinates[0][0][0]
                },
                {
                    latitude: this.props.points.data.routeGeoJSON.features[0].geometry.coordinates[0][1][1],
                    longitude: this.props.points.data.routeGeoJSON.features[0].geometry.coordinates[0][1][0]
                },
                {
                    latitude: this.props.points.data.routeGeoJSON.features[0].geometry.coordinates[0][2][1],
                    longitude: this.props.points.data.routeGeoJSON.features[0].geometry.coordinates[0][2][0]
                },
                {
                    latitude: this.props.points.data.routeGeoJSON.features[0].geometry.coordinates[0][3][1],
                    longitude: this.props.points.data.routeGeoJSON.features[0].geometry.coordinates[0][3][0]
                }
            ]
        };
    }
    
    onButtonPressClose = () => {
        this.props.navigation.navigate('Home');
    }
    
    render() {
        const { polygon } = this.state;
        return (
            <View style={styles.container}>
                <MapView 
                    provider={this.props.provider}
                    style={styles.map}
                    zoomEnabled
                    initialRegion={{
                        latitude: this.props.points.data.routeGeoJSON.features[0].geometry.coordinates[0][0][1],
                        longitude: this.props.points.data.routeGeoJSON.features[0].geometry.coordinates[0][0][0],
                            latitudeDelta: 0.0922,
                            longitudeDelta: 0.0421,
                        }}
                >
                    <MapView.Polygon
                        coordinates={polygon}
                        fillColor="rgba(0, 200, 0, 0.5)"
                        strokeColor="rgba(0,0,0,0.5)"
                        strokeWidth={2}
                    />
                </MapView>
                <View style={stylesContainer.topContainer}>
                    <Card title="Route Information">
                        <View>
                            <Text>Name: {this.props.points.data.name}</Text>
                            <Text>Description: {this.props.routes.data.description}</Text>
                        </View>
                    </Card>
                </View>
                <View style={stylesContainer.buttonBottomContainer}>
                        <Button
                            medium
                            title="Back"
                            backgroundColor="#94b8b8"
                            onPress={this.onButtonPressClose}
                        />
                </View> 
            </View>
        );
        }
    }
    
    PolygonPage.propTypes = {
        provider: MapView.ProviderPropType,
    }; 
    
    const styles = StyleSheet.create({
        container: {
            ...StyleSheet.absoluteFillObject,
            justifyContent: 'flex-end',
            alignItems: 'center',
        },
        map: {
            flex: 1,
            height: window.height,
            width: window.width
        }
    });
    
    function mapStateToProps({ points }) {
        return { points };
    }
    
    export default connect(mapStateToProps)(PolygonPage);
    

    This works for me as it rendered a polygon from the point that I got from API.

    My API response is in JSON. This is the example

    {
    "timestamp": 000111,
    "verb": "GET",
    "object": "route",
    "data": {
        "description": "describe",
        "routeGeoJSON": {
            "type": "FeatureCollection",
            "features": [
                {
                    "geometry": {
                        "type": "Polygon",
                        "coordinates": [
                            [
                                [
                                    122.18919, // this is longitude
                                    4.82948294 // this is latitude
                                ],
                                [
                                    124.17318,
                                    5.9319319
                                ],
                                [
                                    101.131191,
                                    2.92492
                                ],
                                [
                                    106.01010192,
                                    4.492472492
                                ]
                            ]
                        ]
                    },
                    "type": "Feature",
                    "properties": {}
                }
            ]
        },
        "id": 1,
        "routeType": "point",
        "name": "Test"
        }
    }
    

    If the points/coordinates is less than 10, than I might probably input it as what I did in my render function. But what if there is more than 10 points(latitude and longitude)? I could not figure how should I use map or for loop function inside this.state = { polygon: [....]}.

    I have searched online and found some example like this and this but failed to grab the understanding of it.

    If anyone have any idea or kind enough to share his suggestion or boilerplate here, I am very thankful for that.

    Thank you.