Opening Google Map onClicking the Button | Reactjs - javascript

I have a map component, in that, I used Google-map-react library to display locations in google map. Inside that map, I am having a button called "View in maps". So, now, if the user clicks on that button. it should take them to the google map with given location coordinates. Please find code below and let me know, how can I achieve that
import React, { Component } from 'react';
import { withStyles } from '#material-ui/core/styles';
import styles from '../../../../../../assets/css/Me.style.js';
import GoogleMapReact from 'google-map-react';
import { Typography, Button } from '#material-ui/core';
import Marker from '#material-ui/icons/LocationOnOutlined'
class DetailsMap extends Component {
static defaultProps = {
center: { lat: 40.7446790, lng: -73.9485420 },
};
render() {
const {classes} = this.props;
return (
<div className={classes.root}>
<div className={classes.googleMap}>
<GoogleMapReact
zoom = {11}
onClick={this.onClick}
defaultCenter={ this.props.center }
>
<Marker
style={{color: '#ff7777'}}
lat={40.7473310}
lng={-73.8517440}
/>
<div className={classes.address}>
<div className={classes.addressWrapper}>
<Typography className={classes.addressHead}>
Address
</Typography>
<Typography className={classes.addressContent}>
221, raj start, doler street<br />
QC J9PB6X
</Typography>
<Button variant='outlined' className={classes.view}>
View in Maps
</Button>
</div>
</div>
</GoogleMapReact>
</div>
</div>
)
}
}
export default withStyles(styles, {withTheme:true})(DetailsMap);

I use this code for show google map in new tab with specific location with marker.
const showInMapClicked = () => {
window.open("https://maps.google.com?q="+your_lat+","+your_lng );
};

Google maps can take GET parameters for coordinates to open on_load. Make an href to this address
https://www.google.com/maps/#${your_lat},${your_lng},${your_desired_zoom}z
Note
To be able to use the formating provided in the above answer you have to use `` quotes over the normal " " or ' '

Related

how to integrate google maps in react using package google-maps-react?

When I use google-maps-react to integrate maps, map loads correctly and even shows the correct markers but I am having some issues.
1- A styling issue normally map component overlays the content I think because it has position absolute but when I change its position to relative it does not show. I have placed it in a div everything above the div shows but it overlays the content below the div.
2- it is giving errors in the console (I am listing them below)
Using UNSAFE_componentWillReceiveProps in strict mode is not recommended and may indicate bugs in your code. Move data fetching code or side effects to componentDidUpdate.If you're updating state whenever props change, refactor your code to use memoization techniques or move it to static getDerivedStateFromProps.
Can't call setState on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to this.state directly or define a state = {}; class property with the desired state in the Wrapper component.
My mapComponent:
import React from "react";
import { Map, Marker, GoogleApiWrapper } from "google-maps-react";
export function MapContainer({ google, captains = [] }) {
const mapStyles = {
width: "85%",
height: "100%",
};
return (
<Map
google={google}
// containerStyle={{
// position: "absolute",
// width: "85%",
// height: "40%",
// }}
// style={mapStyles}
containerStyle={mapStyles}
initialCenter={{
lat: captains[0].location.coordinates[1],
lng: captains[0].location.coordinates[0],
}}
zoom={captains.length === 1 ? 18 : 13}
>
{captains.map((captain) => (
<Marker
key={captain._id}
position={{
lat: captain.location.coordinates[1],
lng: captain.location.coordinates[0],
}}
/>
))}
</Map>
);
}
export default GoogleApiWrapper({
apiKey: "#############################",
})(MapContainer);
Here is where I am using it:
import MapContainer from "../components/MapContainer";
function Captains({ captains }) {
return (
<div className=" h-screen overflow-auto">
<div className="pt-24 pl-20 flex flex-col">
<othercontent ..... />
<div className="visible h-1/3 w-10/12 mx-5">
<MapContainer captains={captains} />
</div>
<PaginationTable
.....
/>
</div>
</div>
</>
);
}

React Google Maps Api doesn't render children elements at first render

I'm trying to use Google Maps Api in my app, everything is fine until I want to display a Marker at the first render of the map. The Marker is not showing up, but if I add one more Marker after the render is done, the Marker will appear.
So the problem is that I want to render the map with a Marker already there, I don't want to wait for some location to be selected.
I want to receive lat and lng from props, but for now I've made an hard coded const (center).
import React, { useMemo } from "react";
import { useJsApiLoader, GoogleMap, Marker } from "#react-google-maps/api";
export default function GoogleMaps({ lat, lng }) {
const { isLoaded } = useJsApiLoader({
googleMapsApiKey: process.env.REACT_APP_GOOGLE_MAPS_API_KEY,
});
const center = useMemo(() => ({ lat: 42.4332, lng: 20.4343 }), []);
if (!isLoaded) {
return <h2>Calculating Locations..</h2>;
}
return (
isLoaded && (
<GoogleMap
center={center}
zoom={17}
mapContainerStyle={{ width: "450px", height: "400px" }}
options={{ disableDefaultUI: true, mapId: "deleted for this snippet" }}
>
<Marker position={center} />
</GoogleMap>
)
);
}
Have you tried importing and using MarkerF instead of Marker?
See: https://github.com/JustFly1984/react-google-maps-api/issues/3048#issuecomment-1166410403
"MarkerF is functional component vs class based Marker component, which does not work with react Strict and/or react#17+"
Also, there are similar issues discussed here: Markers not rendering using #react-google-maps/api and here: Map Marker don't show up (Marker rendered before Map)- ReactJS with #react-google-maps/api

Keep updating markers on map on top of an overlay image - google maps react

I have the following code which has an overlay image on the map.
However, i would like to display markers on top of this overlay image. The markers come from a users array which is constantly being updated. I want to loop through these markers and place them on the map, but keep updating them if they change longitude/latitude.
I have tried some code below, the data is getting passed to the components correctly, but the markers are always staying in the same position. Could anyone tell me what i'm doing wrong?
Below is my main map code
return (
<div className='map'>
<div className='google-map'>
<GoogleMapReact
bootstrapURLKeys={{ key: 'KEYID' }}
defaultCenter={location}
defaultZoom={zoomLevel}>
{users?.map((user, i) => {
return (
<MyGreatPlace
key={i}
latitude={user.latitude}
longitude={user.longitude}
sender={user.sender}
/>
);
})}
<OverlayImage lat={location.lat} lng={location.lng} text={'A'} />
</GoogleMapReact>
</div>
</div>
);
Below is MyGreatPlace.jsx file for the markers:
export default function MyGreatPlace({ i, latitude, longitude, sender }) {
console.log(sender);
return (
<div style={{ width: 60, height: 60, background: 'green' }}>
{latitude} {longitude} {sender}
</div>
);
}
Below is OverlayImage.jsx file for the overlay image:
export default class OverlayImage extends Component {
static propTypes = {
text: PropTypes.string,
};
static defaultProps = {};
shouldComponentUpdate = shouldPureComponentUpdate;
render() {
return <div>{<img src={floorplan} alt='floorplan' />}</div>;
}
}
Do i need to make the MyGreatPlace component the same as the overlay component? I'm not sure how props would work getting the longitude/latitude passed in?
The solution was to change MyGreatPlace file to the same as the overlay component, to look like this:
export default class MyGreatPlace extends Component {
static propTypes = {
text: PropTypes.string,
};
static defaultProps = {};
shouldComponentUpdate = shouldPureComponentUpdate;
render() {
return <div>{this.prop.text}</div>;
}
}

React Tooltip using Components

I have a google maps widget on my page with markers on it to represent specific locations. I have made it so that when you click on the marker, a tooltip will appear above it to show more information. I am using react tooltip. Here's my index.jsx
<GoogleMaps
bootstrapURLKeys={{ key: /* KEY */ }}
center={center}
zoom={zoom}
>
<ReactTooltip
id="google"
html
multiline
/>
{markers.map((marker) => (
<Marker
key={marker.key}
place={marker}
lat={marker.lat}
lng={marker.lng}
/>
))}
</GoogleMaps>
Here's my Marker.jsx
import React from 'react';
import MarkerWindow from "./MarkerWindow"
const Marker = ({ place }) => {
const tip =
<>
<MarkerWindow place={place}/>
</>;
return (
<>
<Wrapper
data-for="google"
data-event="click focus"
data-tip={tip}
/>
</>
)
};
export default Marker;
And then my MarkerWindow.jsx
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
const MarkerWindow = ({ place }) =>
(
<StyledSection>
<h3>${place.name}</h3>
<p>
${place.address}
<br />
${place.number}
<br />
</p>
</StyledSection>
);
const StyledSection = styled.div`
width: 18px;
height: 18px;
background-color: #e01414;
`;
MarkerWindow.propTypes = {
place: PropTypes.object.isRequired,
};
export default MarkerWindow;
But when I load the page, I get this:
How do I load custom components into ReactTooltip?
react-tooltip doesn't support components as the tooltip content, but accepts html strings.
You should use the data-html={true} prop to let the react-tooltip know that the value passed to data-tip is actually an HTML code.
Then you should provide the html of the tooltip content as a string.
So this should work:
const Marker = ({ place }) => {
const tip = `
<div style="width: 18px; height: 18px; background-color: '#e01414';" >
<h3>${place.name}</h3>
<p>
${place.address}
<br />
${place.number}
<br />
</p>
</div>
`;
return (
<>
<Wrapper
data-for="google"
data-event="click focus"
data-html={true}
data-tip={tip}
/>
</>
)
};
I converted your MarkerWindow component to an HTML string by inlining the styles from the styled-component.
However, if you are not limmited to use react-tooltip I suggest to use other packages that let you render react components for a tooltip content. Take a look at #tippyjs/react for example.

Remove Zoom control from map in react-leaflet

I am building a react-leaflet application, and I am trying to separate the zoom control from the map itself. The same question in a vanilla Leaflet context was asked here: Placing controls outside map container with Leaflet?. This is what I'm trying to accomplish within the react-leaflet framework. Here is the general outline of my project:
import UIWindow from './UIWindow'
import Map from './MapRL'
class App extends React.Component {
render () {
return (
<div className="App">
<Map />
<UIWindow />
</div>
)
}
}
export default App;
My map component looks like this:
import React from 'react'
import { Map as LeafletMap, TileLayer } from 'react-leaflet'
class Map extends React.Component {
render () {
return(
<LeafletMap
className="sidebar-map"
center={...}
zoom={...}
zoomControl={false}
id="mapId" >
<TileLayer
url="..."
attribution="...
/>
</LeafletMap>
)
}
}
export default Map;
Then my UIWindow looks like this:
class UIWindow extends React.Component {
render () {
return(
<div className="UIWindow">
<Header />
<ControlLayer />
</div>
)
}
}
And finally, my ControlLayer (where I want my ZoomControl to live) should look something like this:
class ControlLayer extends React.Component {
render () {
return (
<div className="ControlLayer">
<LeftSidebar />
<ZoomControl />
{this.props.infoPage && <InfoPage />}
</div>
)
}
}
Of course with this current code, putting ZoomControl in the ControlLayer throws an error: TypeError: Cannot read property '_zoom' of undefined, with some more detailed writeup of what's wrong, with all the references the Leaflet's internal code regarding the zoom functionality.
(DomUtil.removeClass(this._zoomInButton, className);, etc.)
I expected an error, because the ZoomControl is no longer a child of the <Map /> component, but rather a grandchild of the <Map />'s sibling. I know react-leaflet functions on its context provider and consumer, LeafletProvider and LeafletConsumer. When I try to call on my LeafletConsumer from within my <ControlLayer />, I get nothing back. For example:
<LeafletConsumer>
{context => console.log(context)}
</LeafletConsumer>
This logs an empty object. Clearly my LeafletConsumer from my ControlLayer is not properly hooked into the LeaflerProvider from my <Map />. Do I need to export the context from the Map somehow using LeafletProvider? I am a little new to React Context API, so this is not yet intuitive for me. (Most of the rest of the app will be using React Redux to manage state changes and communication between components. This is how I plan to hook up the contents of the sidebar to the map itself. My sidebar doesn't seem to have any problem with being totally disconnected from the <Map />).
How can I properly hook this ZoomControl up to my Map component?
UPDATE:
I tried capturing the context in my redux store, and then serving it to my externalized ZoomControl. Like so:
<LeafletConsumer>
{ context => {
this.props.captureContext(context)
}}
</LeafletConsumer>
This captures the context as part of my redux store. Then I use this as a value in a new context:
// ControlLayer.js
const MapContext = React.createContext()
<MapContext.Provider value={this.props.mapContext}>
<LeftSidebar />
<MapContext.Consumer>
{context => {
if (context){
console.log(ZoomControl);
}
}}
</MapContext.Consumer>
</MapContext.Provider>
Where this.props.mapContext is brought in from my redux matchStateToProps, and its exactly the context captured by the captureContext function.
Still, this is not working. My react dev tools show that the MapContent.Consumer is giving the exact same values as react-leaflet's inherent '' gives when the ZoomControl is within the Map component. But I still get the same error message. Very frustrated over here.
Here is the same approach without hooks:
the Provider should look like this:
class Provider extends Component {
state = { map: null };
setMap = map => {
this.setState({ map });
};
render() {
return (
<Context.Provider value={{ map: this.state.map, setMap: this.setMap }}>
{this.props.children}
</Context.Provider>
);
}
}
Leaflet component will be:
class Leaflet extends Component {
mapRef = createRef(null);
componentDidMount() {
const map = this.mapRef.current.leafletElement;
this.props.setMap(map);
}
render() {
return (
<Map
style={{ width: "80vw", height: "60vh" }}
ref={this.mapRef}
center={[50.63, 13.047]}
zoom={13}
zoomControl={false}
minZoom={3}
maxZoom={18}
>
<TileLayer
attribution='&copy OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?"
/>
</Map>
);
}
}
and now to access the setMap function to the compoenntDidMount you need to do the following:
export default props => (
<Context.Consumer>
{({ setMap }) => <Leaflet {...props} setMap={setMap} />}
</Context.Consumer>
);
For the rest take a look here: Demo
I am not sure how to achieve that using your approach where react-leaflet's wrapper ZoomControl is not a child of Map wrapper when you try to place it outside the Map wrapper.
However, for a small control like the ZoomControl, an easy solution would be to create a custom Zoom component, identical to the original, construct it easily using the native css style and after accessing the map element, invoke the zoom in and out methods respectively.
In the below example I use react-context to save the map element after the map loads:
useEffect(() => {
const map = mapRef.current.leafletElement;
setMap(map);
}, [mapRef, setMap]);
and then here use the map reference to make a custom Zoom component identical to the native (for css see the demo):
const Zoom = () => {
const { map } = useContext(Context);
const zoomIn = e => {
e.preventDefault();
map.setZoom(map.getZoom() + 1);
};
const zoomOut = e => {
e.preventDefault();
map.setZoom(map.getZoom() - 1);
};
return (
<div className="leaflet-bar">
<a
className="leaflet-control-zoom-in"
href="/"
title="Zoom in"
role="button"
aria-label="Zoom in"
onClick={zoomIn}
>
+
</a>
<a
className="leaflet-control-zoom-out"
href="/"
title="Zoom out"
role="button"
aria-label="Zoom out"
onClick={zoomOut}
>
−
</a>
</div>
);
};
and then place it wherever you like:
const App = () => {
return (
<Provider>
<div style={{ display: "flex", flexDirection: "row" }}>
<Leaflet />
<Zoom />
</div>
</Provider>
);
};
Demo

Categories

Resources