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.
Related
I am using react leaflet my next.js app.React Leaflet marker is broken see image. I am using custom market but not work. I 've put together a very simple React / Leaflet demo but the marker icon is not showing at all
import { useState, useEffect, useRef } from "react";
import "leaflet/dist/leaflet.css";
import {
MapContainer,
TileLayer,
Marker,
Popup,
useMap,
useMapEvents,
} from "react-leaflet";
import { useSelector } from "react-redux";
import { useCallback } from "react";
import { useMemo } from "react";
function LocationMarker({ lat, lng }) {
const [position, setPosition] = useState(null);
const [bbox, setBbox] = useState([]);
const map = useMap();
useEffect(() => {
map.flyTo([lat, lng], map.getZoom());
}, [lat]);
return position === null ? null : (
<Marker position={position}>
<Popup>You are here</Popup>
</Marker>
);
}
export default function EditAddedMap({ lat, lng, setLat, setLng }) {
const { resturant } = useSelector((state) => state);
return (
<MapContainer center={[lat, lng]} zoom={13} scrollWheelZoom={false}>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<LocationMarker lat={lat} lng={lng} />
<DraggableMarker lat={lat} lng={lng} setLng={setLng} setLat={setLat} />
</MapContainer>
);
}
Try to install leaflet-defaulticon-compatibility found here https://www.npmjs.com/package/leaflet-defaulticon-compatibility
And import it accordingly:
import "leaflet-defaulticon-compatibility";
import "leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.css";
I have this React code:
import React, { Component } from 'react';
import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet';
import 'leaflet/dist/leaflet.css';
import './Map.css';
import L from 'leaflet';
import { geolocated } from 'react-geolocated';
class Map extends Component {
render() {
const DEFAULT_LATITUDE = 32.313268;
const DEFAULT_LONGITUDE = 35.022895;
const latitude = this.props.coords ? this.props.coords.latitude : DEFAULT_LATITUDE;
const longitude = this.props.coords ? this.props.coords.longitude : DEFAULT_LONGITUDE;
return (
<MapContainer className="leaflet-map" center={[latitude, longitude]} zoom={17} scrollWheelZoom={true}>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Marker position={[latitude, longitude]}>
<Popup>
Here you are ^_^
</Popup>
</Marker>
</MapContainer>
);
}
}
export default Map;
How can I add an event to get latitude and longitude of the position when the map clicked?
In the leaflet documentation there is something like:
map.on('click', function(e) {
alert("Lat, Lon : " + e.latlng.lat + ", " + e.latlng.lng)
});
But how can I do this with ReactJS?
First take the map instance using whenCreated prop on MapContainer
<MapContainer
className="leaflet-map"
center={[latitude, longitude]}
zoom={17}
scrollWheelZoom={true}
style={{ height: "100vh" }}
whenCreated={(map) => this.setState({ map })}
>
Second use it on componentDidUpdate when it is defined to listen to on map click event
componentDidUpdate(prevProps, prevState) {
const { map } = this.state;
if (prevState.map !== map && map) {
map.on("click", function (e) {
alert("Lat, Lon : " + e.latlng.lat + ", " + e.latlng.lng);
});
}
}
Demo
Actually, I am using react-leaflet with custom popup, when i tried to use react-router-dom Link component in it than i got error
"leaflet": "^1.4.0",
"leaflet.markercluster": "^1.3.1",
"react": "^16.5.2",
"react-dom": "^16.8.4",
"react-router-dom": "4.3.1",
"react-router-redux": "5.0.0-alpha.9",
React Router Link doesn't work with LeafletJS
I tried above mentioned link but still got error.
*
* CustomPopup
*
*/
import React from 'react';
import Moment from 'moment';
import {extendMoment} from "moment-range";
import {Link, withRouter} from "react-router-dom";
import currencyFormatter from "../../utils/currencyFormatter";
const moment = extendMoment(Moment);
const CustomPopup = (props) => {
const { item } = props;
return (
<div className='map-properties'>
<div className='map-img'>
<img
style={{width: "301px", height: "203px"}}
src={item.image ? `${item.image}`: "http://placehold.it/301x203"}
className="custom-img"
onError={(e)=>{e.target.onerror = null; e.target.src="http://placehold.it/301x203"}}
/>
</div>
<div className='map-content'>
<h4><a href='properties-details.html'> {item.title} </a></h4>
{item.address && <p className='address'> <i className='fa fa-map-marker'></i> {item.address} </p>}
<p className='description'> description </p>
<div className='map-properties-fetures'>
<span><i className='flaticon-square-layouting-with-black-square-in-east-area'></i>{item.area} sqft<sup>2</sup></span>
<span className="right"><i className='flaticon-bed'></i>{item.bedrooms}</span>
<span><i className='flaticon-holidays'></i>{item.bathrooms}</span>
<span className="right">
<i className="fa fa-calendar"></i> {moment.range(moment(item.created_at), moment()).diff('days')} Days ago
</span>
</div>
<div className='map-properties-btns'>
<a className='border-button-sm border-button-theme' style={{marginRight: "5px"}}>
{currencyFormatter(item.price)}
</a>
##below the React Router Dom component <Link/>##
<Link href='properties-details' className='button-sm button- theme'>Details</Link>
</div>
</div>
</div>
);
};
export default CustomPopup;
/**
*
*End of CustomPopup
*
*/
/**
*
* LeafLet Map Container where i use above custom popup
*
*/
import "./index.css";
import React, {Fragment, PropTypes } from 'react';
import CustomPopup from "../../components/CustomPopup";
import {createClusterCustomIcon, ThemePointer} from "../../components/CustomMarker";
import 'react-leaflet-markercluster/dist/styles.min.css';
import MarkerClusterGroup from 'react-leaflet-markercluster';
import {TileLayer, Marker, Popup, Map, LayersControl,} from 'react-leaflet';
import {withRouter} from "react-router-dom";
import ContextProvider from "../../components/ContextProvider";
export const Context = React.createContext({});
const MyPopupMarker = (props) => {
const { item } = props;
let lat = item.latitude ? item.latitude : 0.0000;
let lng = item.longitude ? item.longitude : 0.0000;
return (
<Marker position={[lat, lng]} icon={ThemePointer}>
<Popup>
<CustomPopup key={item.id} item={item} />
</Popup>
</Marker>
);
};
const MyMarkersList = (props) => {
const { markers } = props;
const items = markers && markers.length > 0 ? markers.map((item, index) => (
<MyPopupMarker key={item.id} item={item} context={props.context}/>
)): "";
return <Fragment>{items ? items : ""}</Fragment>;
};
const { BaseLayer,} = LayersControl;
class LeafLetMap extends React.PureComponent{
updateDimensions() {
const height = window.innerWidth >= 992 ? window.innerHeight : 400
this.setState({ height: height - 348 })
}
componentWillMount() {
this.updateDimensions()
}
componentDidMount() {
window.addEventListener("resize", this.updateDimensions.bind(this))
console.log(this.props.context)
}
componentWillUnmount() {
this._isMounted = false;
window.removeEventListener("resize", this.updateDimensions.bind(this))
}
render(){
const {filterPropertiesList, featuredPropertyList} = this.props;
let markers;
if(!filterPropertiesList.isSearched && featuredPropertyList.data.length > 0){
markers = featuredPropertyList.data;
}else if(filterPropertiesList.isSearched && filterPropertiesList.data.properties.length > 0){
markers = filterPropertiesList.data.properties;
}
return (
<Map style={{ height: this.state.height }} center={[25.0657, 55.17128]} zoom={16}>
<LayersControl position="bottomleft">
<BaseLayer checked name="Default">
<TileLayer
attribution='© OpenStreetMap contributors'
url="http://{s}.google.com/vt/lyrs=p&x={x}&y={y}&z={z}"
subdomains={['mt0','mt1','mt2','mt3']}
/>
</BaseLayer>
<BaseLayer name="Satellite">
<TileLayer
attribution='© OpenStreetMap contributors'
url="http://{s}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}"
subdomains={['mt0','mt1','mt2','mt3']}
/>
</BaseLayer>
</LayersControl>
<MarkerClusterGroup iconCreateFunction={createClusterCustomIcon}>
<MyMarkersList markers={markers} />
</MarkerClusterGroup>
</Map>
);
}
}
export default LeafLetMap;
Warning: Failed prop type: The prop to is marked as required in Link, but its value is undefined.
Warning: Failed context type: The context router is marked as
required in Link, but its value is undefined.
I mentioned libraries versions, post container and custom popup where i used react router dom link component.
I also paste occurred warnings as a results.
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;
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}
/>