How can i add custom marker to map using react-map-gl? - reactjs

i want to add custom marker to my map. Im using react-map-gl library and components Layer and Source. My code is:
import pin from "../images/pin.svg";
. . . .
const geojson: GeoJSON.Feature<GeoJSON.Geometry> = {
type: 'Feature' ,
geometry:{
type: 'Point' as 'Point',
coordinates:[21.300465619950796 , 48.70795452044564],
},
properties: {
name: "Marker",
}
};
const layerStyle = {
id: 'point',
type: 'symbol' as 'symbol',
source: 'Marker',
layout: {
'icon-image': 'pin',
'icon-size': 1
},
paint: {
'icon-color': '#78546'
},
};
.
.
.
.
<Source id="my-data" type="geojson" data={geojson}>
<Layer {...layerStyle} />
</Source>
But my icon doesnt appear. Can you help me please?

Basically mapbox needs to have a reference of the image already stored before it can assigned it to a pin/marker in a layer symbol.
Here is what I did, and hope it helps you
First create a onLoad function that will be called when the map loads
<Map
...
ref={mapRef} // need a ref to reference it later if you don't have it already
onLoad={onLoad}
/>
In the onLoad function create a new image and load its content
const onLoad = (e) => {
if (mapRef.current) {
const pinImage = new Image();
pinImage.onload = () => {
if (!mapRef.current.hasImage('pin')) {
mapRef.current.addImage('pin', pinImage, { sdf: true });
}
}
pinImage.src = pin; // pin is your svg import
}
}
It should now work with your current layer attributes, please note that the name given in the addImage call (the first argument 'pin') must match the image-icon in the layer settings.
Note: Also make sure the layer style source attribute matches that of its source id.

Something like this:
import ReactMapGL, {Marker} from 'react-map-gl';
<ReactMapGL width="100vw" height="100vh">
<Marker longitude={longitude} latitude={latitude} >
<img src={pin} />
</Marker>
</ReactMapGL>

Related

polylinedacorator with react leaflet 4

I am trying to include arrows to the Polyline in react-leaft. For that I am using polylinedecorator plugin. There is a similar post on this platform. However, it uses withLeaflet module which is not supported in react-leaflet 4.0. How can I make it run without using 'withLeaflet'.
I have tried to implement it with the hooks. However, it does not work and need some assistance, how can I make it run.
export default function App(): JSX.Element {
const polylineRef = useRef<any>(null);
const arrow = [
{
offset: "100%",
repeat: 0,
symbol: L.Symbol.arrowHead({
pixelSize: 15,
polygon: false,
pathOptions: { stroke: true }
})
}];
useEffect(()=>{
L.polylineDecorator(polylineRef.current,{
patterns: arrow
})
}, [polylineRef]);
return (
<MapContainer center={center} zoom={13} scrollWheelZoom={true} style={{height: 'calc(100% - 30px)'}}>
<TileLayer
attribution='© OpenStreetMap contributors'
url='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
/>
{currentData?.movingActors.map(line =>(<Polyline key={line.id}
positions={[line.startLocation, line.endLocation] } ref={polylineRef}
color={modeColor(line.mode)}
/>
))}
</MapContainer>
</>);}
CHANGES MADE TO THE ACCEPTED ANSWER TO MAKE IT RUN
function PolylineDecorator({ patterns, polyline,color }) {
const map = useMap();
useEffect(() => {
if (!map) return;
L.polyline(polyline, {color}).addTo(map); // added color property
L.polylineDecorator(polyline, {
patterns,
}).addTo(map);
}, [map]);
return null;
}
{currentData?.movingActors.map(line =>(<PolylineDecorator key={line.id} patterns ={arrow} polyline={position} color = {modeColor(line.mode)} />) ) } //here I used color parameters to dynamically add colors
What you need is a custom react functional component that returns null and has a useEffect with the code to initialize the plugin:
function PolylineDecorator({ patterns, polyline }) {
const map = useMap();
useEffect(() => {
if (!map) return;
L.polyline(polyline).addTo(map);
L.polylineDecorator(polyline, {
patterns
}).addTo(map);
}, [map]);
return null;
}
and then use it like:
<MapContainer...>
<TileLayer url="http://{s}.tile.osm.org/{z}/{x}/{y}.png" />
<PolylineDecorator patterns={arrow} polyline={polyline} />
</MapContainer>
Demo

Custom button on the leaflet map with React-leaflet version3

I'm a new leaflet learner with React typescript. Want to create a custom button on the map. On clicking the button a popup will appear. I saw many example but they are all based on older version and I also tried to create my own but no luck. The documentation also not providing much help. Even a functional custom control component is also very effective for my app. Any help on this will be much appreciated. Here is my code,
Custom button
import React, { Component } from "react";
import { useMap } from "react-leaflet";
import L, { LeafletMouseEvent, Map } from "leaflet";
class Description extends React.Component<{props: any}> {
createButtonControl() {
const MapHelp = L.Control.extend({
onAdd: (map : Map) => {
const helpDiv = L.DomUtil.create("button", ""); //how to pass here the button name and
//other property ?
//a bit clueless how to add a click event listener to this button and then
// open a popup div on the map
}
});
return new MapHelp({ position: "bottomright" });
}
componentDidMount() {
const { map } = this.props as any;
const control = this.createButtonControl();
control.addTo(map);
}
render() {
return null;
}
}
function withMap(Component : any) {
return function WrappedComponent(props : any) {
const map = useMap();
return <Component {...props} map={map} />;
};
}
export default withMap(Description);
The way I want to call it
<MapContainer
center={defaultPosition}
zoom={6}
zoomControl={false}
>
<Description />
<TileLayer
attribution="Map tiles by Carto, under CC BY 3.0. Data by OpenStreetMap, under ODbL."
url="https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png"
/>
<ZoomControl position={'topright'}/>
</MapContainer>
You're close. Sticking with the class component, you just need to continue creating your buttons instance. You can use a prop on Description to determine what your button will say and do:
<Description
title={"My Button Title"}
markerPosition={[20.27, -157]}
description="This is a custom description!"
/>
In your decsription's createButtonControl, you're almost there. You just need to fill it out a bit:
createButtonControl() {
const MapHelp = L.Control.extend({
onAdd: (map) => {
const helpDiv = L.DomUtil.create("button", "");
this.helpDiv = helpDiv;
// set the inner content from the props
helpDiv.innerHTML = this.props.title;
// add the event listener that will create a marker on the map
helpDiv.addEventListener("click", () => {
console.log(map.getCenter());
const marker = L.marker()
.setLatLng(this.props.markerPosition)
.bindPopup(this.props.description)
.addTo(map);
marker.openPopup();
});
// return the button div
return helpDiv;
}
});
return new MapHelp({ position: "bottomright" });
}
Working codesandbox
There's a million ways to vary this, but hopefully that will get you going.

React Images Won't Load when I pass my src value through array

So when I use src={require('images/img-1')} my image loads fine
However, I'm trying to pass in the src through a data component, so the code would look like this
export const CardData = [
{
src: '../images/img-2.jpg',
text: 'Travel',
},
Then I map through my code and would use
{CardData.map((item, index) => {
<img
src={item.src} />
But this doesn't work because I can't add require in it. How would I write the same code but include require?
I tried src={require{item.src}} but it didn't work for me
First of all you should map your array like this
{ CardData.map((item, index) =>
<img
src={item.src}
/>
}
Then you don't need to require the image in the tag and you just need to import it at the top
like this
import MyImg from '../images/img-2.jpg';
// use MyImg allias for using that image
export const CardData = [
{
src: MyImg,
text: 'Travel',
},

Markers not working with google-map-react

I'm working with the 'google-map-react' library and I have tried all but the markers are not showing up.
I pass the coords to the marker in many ways but none worked.
Here's my code & repository:
https://github.com/jorginyu/ubica
import React, { Component } from 'react';
import GoogleMapReact from 'google-map-react';
const API_KEY = 'WTFULOOKINAT';
const contacts = [
{ name: 'Spiderman', lat: 41.529616, lng: 2.434130 },
{ name: 'Iron Man', lat: 41.528103, lng: 2.433834 },
{ name: 'Hulk', lat: 41.530192, lng: 2.422994 }
];
const MarkersC = (text ) => <div className="contact">{text}</div>;
export default class MapComponent extends Component {
constructor(props) {
super(props);
this.state = {
center: {
lat: 41.528452,
lng: 2.434195
},
zoom: 18
}
}
render() {
return (
// Important! Always set the container height explicitly
<div className="mt-5" style={{ height: '80vh', width: '100%' }}>
<GoogleMapReact
bootstrapURLKeys={{ key: API_KEY }}
defaultCenter={this.state.center}
defaultZoom={this.state.zoom}
>
{contacts.map((contact,i) => {
<MarkersC position={{lat: contact.lat, lng: contact.lng}} text={contact.name} key={i} />
})}
</GoogleMapReact>
</div>
);
}
}
What can I do? Thanks for your time :)
There is a formatting problem. I deleted the spaces:
THEN:
{
contacts.map((contact, i) =>
<MarkersC lat={contact.lat} lng={contact.lng} text={contact.name} key={i} />
)
}
NOW:
{
contacts.map((contact, i) => <MarkersC lat={contact.lat} lng={contact.lng} text={contact.name} key={i} /> )
}
If you open the browser's console, you will see an error. The problem is with your MarkerC component and how you try to get the text prop.
The parameter of the component is an object with all properties that are passed to it.
You do not destructure it to get the text you simply use the whole parameter and try to display it.
So you need to propertly destructure it as const MarkersC = ( {text} ) => ..
Instead of
const MarkersC = ( text ) => <div className="contact">{text}</div>;
it should be
const MarkersC = ( {text} ) => <div className="contact">{text}</div>;
Update
Just noticed, the google-map-react expect to find lat and lng properties on the marker. You have wrapped them inside a position property so they cannot be found.
So your usage should be
either
<MarkersC lat={contact.lat} lng={contact.lng} text={contact.name} key={i} />
or spread the whole contact object that holds those properties
<MarkersC {...contact} key={i} />
so that the lat,lng and text are all direct properties of the MarkersC component.
Declaring the object type and referencing directly the value of 'text' on the initial 'const' worked for me:
const AnyReactComponent = (text:any) => <div>{text.text}</div>;
In your question, you have provided invalid GoogleMap key so it is showing error in console Google Maps JavaScript API error: InvalidKeyMapError.
So provide a valid GoogleMap key or blank(const API_KEY = '') for development use only.
In my case with blank API_KEY, it's working fine.
(Your git repo code is different than code you have posted here in StackOverflow.)

How to add default Options for markers

I want to add a different icon to the markers that are created when i click with the marker drawing control.(react-google-maps package)
I tried addding a markerOptions prop in the DrawingManager component but it doesnt seem to work like polygonOptions work.
<GoogleMap defaultZoom={13} defaultCenter={{ lat: 38.022871, lng: 23.790431 }}>
<DrawingManager
ref={props.onDrawingManagerMounted}
defaultDrawingMode={this.state.currentDrawingMode}
defaultOptions={{
drawingControl: true,
drawingControlOptions: {
position: google.maps.ControlPosition.TOP_CENTER,
drawingModes: [
google.maps.drawing.OverlayType.POLYGON,
google.maps.drawing.OverlayType.MARKER
]
},
polygonOptions: this.colorOptions(),
markerOptions: {
icon: {
url: require("../../../assets/images/helipadIcon.png"),
anchor: new google.maps.Point(5, 58)
}
}
}}
Quick tipp: name the package that you are using before you ask your question. It took me a while to find the react-google-maps package on npm.
Check out the official documentation of the named package: https://tomchentw.github.io/react-google-maps/
You will find out that the DrawingManager component doesn't offer a prop named markerOptions or polygonOptions. Instead use the Marker component (https://tomchentw.github.io/react-google-maps/#marker) which offers a property icon of type any.
<Marker icon={} .../>
If you want to change/edit markers that have been drawn using the DrawingManager, you can use the onMarkerComplete callback function that will return the marker object. You can use the marker object to change the icon. See: https://developers.google.com/maps/documentation/javascript/reference/drawing#DrawingManager.markercomplete
const icon = {
url: require("../../../assets/images/helipadIcon.png"),
anchor: new google.maps.Point(5, 58)
};
const onMarkerComplete = (marker) => {
marker.setIcon(icon);
}
return (
<DrawingManager onMarkerComplete={onMarkerComplete} ...>
...
</DrawingManager>
);

Resources