React-Leaflet draw circle markers on map - reactjs

I'm new to Leaflet/React-Leaflet and I am trying to plot a circle marker on a map and I'm having trouble. React-Leaflet's documentation is not very good. I have figured out how to add a popup that shows on click though.
Here's my code:
import React from 'react';
import { render } from 'react-dom';
import { Map, Marker, Popup, TileLayer, Tooltip, Circle } from 'react-leaflet';
import DivIcon from 'react-leaflet-div-icon';
import axios from 'axios';
export default class MapView extends React.Component {
constructor(props) {
super(props)
this.state = {
nasaLocations: [],
spacexLocations: []
};
}
componentDidMount() {
axios.get(`https://data.nasa.gov/resource/gvk9-iz74.json`)
.then(res => {
const nasaData = res.data;
this.setState({nasaLocations: nasaData})
console.log(this.state.nasaLocations);
})
axios.get(`https://api.spacexdata.com/v2/launchpads`)
.then(res => {
const spacexData = res.data;
this.setState({spacexLocations: spacexData})
console.log(this.state.spacexLocations);
})
}
render() {
const position = [40.730610, -73.935242];
return (
<Map
style={{height: "100vh"}}
center={position}
zoom={3}>
<TileLayer
url="https://api.mapbox.com/styles/v1/nicknyr/cje7mtk2y6gf92snsydobiahf/tiles/256/{z}/{x}/{y}?access_token=pk.eyJ1Ijoibmlja255ciIsImEiOiJjajduNGptZWQxZml2MndvNjk4eGtwbDRkIn0.L0aWwfHlFJVGa-WOj7EHaA"
attribution="<attribution>" />
<Marker position={position}>
<Popup>
<span>
A pretty CSS3 popup. <br /> Easily customizable.
</span>
</Popup>
</Marker>
</Map>
);
}
}
How can I render a circle on those position coordinates? I've attemped to add a type tag within the Map tag but I can't figure out the proper syntax and how to properly add lat/long coordinates.

I have little experience with Leaflet, but I got a circle to render, here is the example. I passed the latlng coords using an obj. Hope this helps. If this didn't answer the question, let me know. Circles show up on each location, with the pop-up showing the details from the space x api.
import React, { Component } from 'react';
import { Map, TileLayer, Marker, Tooltip,Popup, Circle} from 'react-leaflet';
import axios from 'axios';
const url = 'https://api.spacexdata.com/v2/launchpads';
const leafURL = "https://api.mapbox.com/styles/v1/nicknyr/cje7mtk2y6gf92snsydobiahf/tiles/256/{z}/{x}/{y}?access_token=pk.eyJ1Ijoibmlja255ciIsImEiOiJjajduNGptZWQxZml2MndvNjk4eGtwbDRkIn0.L0aWwfHlFJVGa-WOj7EHaA";
class App extends Component {
constructor(props){
super(props);
this.state = {
latlng: {
lat: 28.5618571,
lng: -80.577366,
},
data: []
}
}
componentWillMount() {
axios.get(url).then(res => {
this.setState({data: res.data})
}).catch(err => {
console.log('error')
})
}
render() {
const {data} = this.state;
console.log(data);
return (
<div>
<Map
style={{height: "100vh"}}
center={this.state.latlng}
zoom={4}>
<TileLayer
url={leafURL}
attribution="<attribution>" />
{data.map((elem, i) => {
return (
<Marker
key={i}
position={{lat:elem.location.latitude, lng: elem.location.longitude}}>
<Popup>
<span>
{elem.full_name}<br />
{elem.status}<br />
{elem.details}<br />
{elem.vehicles_launched.map((elem, i) => {
return ( <p key={i}>{elem}</p>)
})}
</span>
</Popup>
<Circle
center={{lat:elem.location.latitude, lng: elem.location.longitude}}
fillColor="blue"
radius={200}/>
</Marker>
)
})}
</Map>
</div>
);
}
}
export default App;

Related

How to get and render points from a database firebase

So basically I was able to get a database which stores points in it.
Now I was able to push each point separately to make this database. Now I need to display these points on the map. This is my code so far:
App.js
import './App.css';
import * as React from "react";
import firebase from "./firebase";
import { ChakraProvider } from "#chakra-ui/react";
import { MapContainer, TileLayer, Marker, Popup, useMapEvents, useMap } from 'react-leaflet'
import 'leaflet/dist/leaflet.css'
import * as L from 'leaflet';
import SearchBar from './SearchBar';
import CovidPoint from './CovidPoint';
import LocationMarkers from './LocationMarkers';
class App extends React.Component {
constructor(props){
super(props)
this.state = {
map: null,
points: [<CovidPoint
position={[43.653226, -79.3831843]}
name="point1"
information="random point"
input = {false}
></CovidPoint>]
}
}
changePos = (pos, zoom) => {
const {map} = this.state;
if (map) map.flyTo(pos, zoom);
}
fetchPoints = (newPoints) => {
// fetch info from database
const ref = firebase.database().ref("points")
const pointsref = ref.push()
pointsref.set(newPoints)
this.setState({points: newPoints})
}
componentDidMount() {
const ref = firebase.database().ref("points")
ref.on("value", snapshot => {
console.log("FireB ",snapshot)
if (snapshot && snapshot.exists()) {
this.setState({points: snapshot.val})
}})
console.log("page loaded!")
}
render() {
return (
<div className="App">
<div id="title">
<h1>CovidStopSpots</h1>
<p>A responsive tracker for Covid-19.</p>
</div>
<div id="map">
<MapContainer
id="1"
center={[43.653226, -79.3831843]}
zoom={13}
scrollWheelZoom={false}
whenCreated={(map) => this.setState({ map })}
style={{ height: "100vh " }}
>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{this.state.points.length > 0 && this.state.points.map(
(point, index) => {
<li key={index}></li>
return point
}) }
{/* <CovidPoint
position={[43.653226, -79.3831843]}
name="point1"
information="random point"
></CovidPoint>
<CovidPoint
position={[50.653226, -79.3831843]}
name="point2"
information="random point"
></CovidPoint> */}
<LocationMarkers points={this.state.points} fetchPoints={this.fetchPoints}></LocationMarkers>
<div id="searchbar">
<SearchBar changePos={this.changePos}/>
</div>
</MapContainer>
</div>
</div>
);
}
}
export default App;
The location Marker file which basically "plots" the points and pushes the to the database.
LocationMarker.js
import { useState } from "react";
import { useMapEvents } from "react-leaflet";
import CovidPoint from "./CovidPoint";
function LocationMarkers(props) {
const [position, setPosition] = useState([]);
useMapEvents({
dblclick(ev) {
console.log("double clicked");
const { lat, lng } = ev.latlng;
setPosition([lat, lng]);
const newPoints = [...props.points];
console.log(newPoints);
newPoints.push(<CovidPoint
position={[lat, lng]}
name="test"
information="test"
input = {true}
></CovidPoint>);
props.fetchPoints(newPoints);
}
});
return null;
}
export default LocationMarkers;
As you can see on this example from documentation, you should use Marker to place a marker. Assuming that you have an array of points (list of coordinates), you can place a list of points like this:
const position = [
[51.505, -0.09],
[51.510, -0.09],
[51.515, -0.09]
]
render(
<MapContainer center={position} zoom={13} scrollWheelZoom={false}>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{points?.map((point)=>(
<Marker position={point}>
)}
</Marker>
</MapContainer>,
)
I don't know what is this CovidPoint. But I think you dont need it.

I'm trying to number my Markers and show InfoWindow with google-maps-react

I can't seem to be able to show the index from my json to google-maps-react, I can see all the markers mapped out, but they show the default marker with no window popped out. Here is the code with the <InfoWindow> placed, react was complaining I have to put a parent div, when I do, I don't see any markers currently.
My car2go json is mapping correctly, just not printing out name={content.name}.
My map.js component:
import React, { Component } from "react";
import Car2go from "../data/car2go/vehicles.json";
import { Map, InfoWindow, Marker, GoogleApiWrapper } from "google-maps-react";
export class MapContainer extends Component {
constructor(props) {
super(props);
this.onMarkerClick = this.onMarkerClick.bind(this);
this.state = {
showingInfoWindow: false,
activeMarker: {},
selectedPlace: {},
name: null
};
}
onMarkerClick(props, marker, e) {
this.setState({
selectedPlace: props,
activeMarker: marker,
showingInfoWindow: true
});
}
render() {
const google = window.google;
const style = {
width: "70%",
height: "70%",
margin: "0 auto"
};
//const google = window.google;
return (
<Map
google={this.props.google}
style={style}
initialCenter={{
lat: 53.59301,
lng: 10.07526
}}
zoom={12}
onClick={this.onMapClicked}
>
{Car2go.placemarks.map((content, index) => {
return (
<div>
<Marker
title={index}
name={content.name}
position={{
lat: content.coordinates[1],
lng: content.coordinates[0]
}}
onClick={this.onMarkerClick}
name={content.name}
/>
<InfoWindow
marker={this.state.activeMarker}
visible={this.state.showingInfoWindow}
>
<div>
<h1>{this.state.selectedPlace.name}</h1>
</div>
</InfoWindow>
</div>
);
})}
</Map>
);
}
}
export default GoogleApiWrapper({
apiKey: "xxxxx"
})(MapContainer);
I guess the React is complaining with error which is similar to this one:
React does not recognize the mapCenter prop on a DOM element
If so, the root cause of this error is related with wrapping Marker component with a div container:
<div>
<Marker
...
/>
</div>
and the way how google-maps-react Map component renders children elements. In that case Map props are transferred to div element instead of Marker component. For a more detail refer Unknown Prop Warning article.
To circumvent this error the following approach could be considered:
replace div container with React.Fragment
explicitly transfer map props to Marker component
Here is an example:
class Map extends Component {
constructor() {
super();
this.state = {
map: null
};
this.handleMapReady = this.handleMapReady.bind(this);
}
handleMapReady(mapProps, map) {
this.setState({ map: map });
}
render() {
return (
<Map
google={this.props.google}
className={"map"}
initialCenter={this.props.center}
zoom={this.props.zoom}
onReady={this.handleMapReady}
>
{this.state.map &&
places.map((place, i) => {
const mapProps = Object.assign({}, this.props);
mapProps.map = this.state.map;
return (
<React.Fragment key={i}>
<Marker
{...mapProps}
onClick={this.handleMarkerClick}
position={place.position}
placeIndex={i}
name={place.name}
/>
</React.Fragment>
);
})}
</Map>
);
}
}
But instead of changes described above I would propose another approach, in particular to create a single instance of InfoWindow component and manage it as demonstrated below:
<Map
google={this.props.google}
className={"map"}
initialCenter={this.props.center}
zoom={this.props.zoom}
>
{places.map((place, i) => {
return (
<Marker
key={i}
onClick={this.handleMarkerClick}
position={place.position}
placeIndex={i}
name={place.name}
/>
);
})}
<InfoWindow
marker={this.state.activeMarker}
visible={this.state.showingInfoWindow}
onClose={this.handleClose}
>
<div>{this.state.selectedPlace.name}</div>
</InfoWindow>
</Map>
Here is a demo

get values into redux form field from another component

I implemented the google map component to view current location and marker drag functionality by using google-maps-react now i want to fill my redux form field with latitude and longitude i tried this way
Location.js
import React, { Component } from "react";
import { Map, InfoWindow, Marker, GoogleApiWrapper } from "google-maps-react";
import { Field, reduxForm, formValueSelector } from "redux-form";
import { connect } from "react-redux";
class Location extends Component {
constructor(props) {
super(props);
this.state = {
lat: null,
lng: null,
markers: []
};
}
componentDidMount() {
navigator.geolocation.getCurrentPosition(position =>
this.setState({
lat: position.coords.latitude,
lng: position.coords.longitude
})
);
}
onMarkerDragEnd = coord => {
const { latLng } = coord;
const lat = latLng.lat();
const lng = latLng.lng();
this.setState({
lat: lat,
lng: lng
});
console.log(lat, lng);
};
render() {
if (!this.props.loaded) {
return <div>Loading...</div>;
}
const style = {
width: "100%",
height: "100vh"
};
return (
<Map
google={this.props.google}
zoom={11}
style={style}
initialCenter={{
lat: this.state.lat,
lng: this.state.lng
}}
center={{
lat: this.state.lat,
lng: this.state.lng
}}
onClick={this.mapClicked}
>
<Marker
title={"Geolocation"}
position={{
lat: this.state.lat,
lng: this.state.lng
}}
draggable={true}
onDragend={(t, map, coord) => this.onMarkerDragEnd(coord)}
/>
</Map>
);
}
}
const selector = formValueSelector("registershop");
const mapStateToProps = state => {
//
//
};
export default GoogleApiWrapper({
apiKey: "......"
})(connect(mapStateToProps)(Location));
Register.JS
import Location from "../common/Location";
class Register extends Component {
const { handleSubmit } = this.props;
return (
<div>
<form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
<label>Lat:</label>
<Field name="lat" component="input" type="number" step="0.01" />
<label>Lng:</label>
<Field name="lng" component="input" type="number" step="0.01" />
</form>
<Location/>
<div>
)
}
when user route to the register component i want to show his/her current location for that i used componentDidMount and also i wanted to give chance to user select their location by drag the marker
I'm new to redux form so i don't have better idea for how to bind longitude and latitude to the reduxform to the submit please help me

React-Leaflet-mapping a list of plots to markers

I've got a React Leaflet map, which renders fine.
I've got a list of plots in state, which appear fine (I can see them if I look at the component state.
Each plot has a GeoJSON polygon property.
I've got a custom marker component, which renders differently based on zoom (either a GeoJSON polygon or a marker at the center of the plot's polygon.)
I'm mapping the plot list and instantiating a custom marker component from each one. But this doesn't render any plots.
What am I missing?
The map component:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../actions';
import { Map, TileLayer, LayersControl, MapControl } from 'react-leaflet';
import { GoogleLayer } from './GoogleLayer';
import { geolocated } from 'react-geolocated';
import 'leaflet-geocoder-mapzen';
import SearchBox from './searchBox';
import Control from 'react-leaflet-control';
import PlotMarker from './plotMarker';
import { centroid } from '#turf/turf';
const { BaseLayer } = LayersControl;
const key = 'MYKEY';
const hybrid = 'HYBRID';
const terrain = 'TERRAIN';
const road = 'ROADMAP';
const satellite = 'SATELLITE';
const centerLat = props => {
if (
props.isGeolocationAvailable &&
props.isGeolocationEnabled &&
props.coords
) {
return props.coords.latitude;
}
return 32.11;
};
const centerLong = props => {
if (
props.isGeolocationAvailable &&
props.isGeolocationEnabled &&
props.coords
) {
return props.coords.longitude;
}
return 34.963;
};
const initialMapCenter = props => {
return [centerLat(props), centerLong(props)];
};
const initialZoomLevel = 11;
const markers = props => {
if (props.plots) {
return (
<div>
{(props.filteredPlots || props.plots).map(
plot =>
plot.feature && (
<PlotMarker
key={plot._id}
geoJSON={plot.feature}
position={centroid(plot.feature).geometry.coordinates}
/>
)
)}
</div>
);
}
};
export class PlotMap extends Component {
render() {
this.props.plots &&
(this.props.filteredPlots || this.props.plots).forEach(plot => {
plot.feature &&
console.log(centroid(plot.feature).geometry.coordinates);
});
return (
<div
className="col-sm-8 m-auto p-0 flex-column float-right"
style={{ height: `85vh` }}>
<Map
center={initialMapCenter(this.props)}
zoom={initialZoomLevel}
zoomControl={true}
onZoomend={e => {
this.props.setZoomLevel(e.target.getZoom());
}}
onMoveEnd={e => {
this.props.setMapCenter(e.target.getCenter());
}}>
<LayersControl position="topright">
<BaseLayer name="Google Maps Roads">
<GoogleLayer googlekey={key} maptype={road} />
</BaseLayer>
<BaseLayer name="Google Maps Terrain">
<GoogleLayer googlekey={key} maptype={terrain} />
</BaseLayer>
<BaseLayer name="Google Maps Satellite">
<GoogleLayer googlekey={key} maptype={satellite} />
</BaseLayer>
<BaseLayer checked name="Google Maps Hybrid">
<GoogleLayer
googlekey={key}
maptype={hybrid}
libraries={['geometry', 'places']}
/>
</BaseLayer>
</LayersControl>
<SearchBox postion="bottomright" />
{markers(this.props)}
</Map>
</div>
);
}
}
function mapStateToProps(state) {
return {
filteredPlots: state.plots.filteredPlots,
plots: state.plots.plots,
mapCenter: state.plots.mapCenter
};
}
export default geolocated({
positionOptions: {
enableHighAccuracy: false
},
userDecisionTimeout: 5000,
suppressLocationOnMount: false
})(connect(mapStateToProps, actions)(PlotMap));
The marker component:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../actions';
import { Marker, GeoJSON } from 'react-leaflet';
export class PlotMarker extends Component {
render() {
const { key, position, geoJSON, zoomLevel } = this.props;
if (zoomLevel > 14) {
return <GeoJSON key={key} data={geoJSON} />;
}
return <Marker key={key} position={position} />;
}
}
function mapStateToProps(state) {
return {
selectedPlot: state.plots.selectedPlot,
plotBeingEdited: state.plots.plotBeingEdited,
zoomLevel: state.plots.zoomLevel
};
}
export default connect(mapStateToProps, actions)(PlotMarker);
The issue turns out to be that GeoJSON uses long-lat, while Leaflet uses lat-long (inherited from Google Maps.) So my markers were appearing in another part of the world. Fixing this is very easy-just call .reverse() on the array of coordinates you're passing into the Marker component as position, like this:
<PlotMarker
key={plot._id}
geoJSON={plot.feature}
position={centroid(plot.feature).geometry.coordinates}
/>

markers not displaying until map moved slightly or clicked in react-map-gl

This marker is not loading until I scroll or drag the map. Even after refreshing, the marker is not displaying. I had hard coded the value. I'm just looking for the marker to display without dragging or clicking.
import React from 'react';
import Icon from 'react-fa';
import ReactMapGL, { Marker } from 'react-map-gl';
export default class EdgeExplorer extends React.Component {
state = {
viewport: {
width: window.innerWidth,
height: window.innerHeight,
latitude: 36.778259,
longitude: -119.417931,
zoom: 4,
mapboxApiAccessToken: 'I will write my token here',
}
}
staticMarker = () => {
return (
<Marker latitude={37.773972} longitude={-122.431297} offsetLeft={-2}
offsetTop={-22}>
<Icon name="map-pin" className="text-white" />
</Marker>
);
}
render() {
const { viewport } = this.state;
return (
<ReactMapGL
{...viewport}
mapStyle='mapbox://styles/mapbox/dark-v9'
onViewportChange={v => this.setState({ viewport: v })
}>
{this.staticMarker()}
</ReactMapGL>
);
}
}
Use ComponentDidMount() and update viewport Value here. It will work fine.

Resources