is it possible to give each marker in react-leaflet individual classname? - react-leaflet

I am using react-leaflet and rc-tabs and while selecting different row in a table I want to display appropriate marker. I want to draw a line from a row to a certain marker when hover with the help of react-lineto, which accepts only classNames as points (well, coordinates too, but this is not suitable for me) so I need an individual class of a marker, a class that can determine a marker on map.
How can I achieve that?
It seems there is no className attribute for every marker, just for group of markers...

okey, after a day spent here I made an marker's icon classnames unique and after that draw a line between my table's hovered row and marker on the map.
return (
<LeafMap center={[deviceCoordinatesLat, deviceCoordinatesLong]} zoom={14} style={{height: "90vh"}}>
<TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
attribution="© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors"
/>
{props.devices.map(device => (
<Marker
className={device.serial}
key={device.id}
position={[degreesToCoord(device.coordinates)[0], degreesToCoord(device.coordinates)[1]]}
icon={hoveredItem.id === device.id ?
markerBigBlue :
new L.Icon({
iconUrl: require('../little-blue-dot.png'),
iconSize: new L.Point(15, 15),
className: "little-blue-dot-" + device.id
})
}
>
{hoveredItem.id === device.id ?
<LineTo
*from={"leaflet-marker-icon little-blue-dot-" + device.id + " leaflet-zoom-animated leaflet-interactive"}*
to={"invisible-blue-dot-" + device.id}
borderWidth={1}
zIndex={100}
borderColor="#0C86B8"
/>
: <div></div>}
<Popup>
<span>{device.model} {device.serial}</span>
</Popup>}
</Marker>
))}
</LeafMap>
)

Related

Leaflet zIndex in custom pane between TileLayer, Geojson

I am organising several layers with an interface which allow order decision. For example I have 3 layers:
WMS Layer, order: 3
Geojson layer: order 2
WMS Layer, order: 1
I am able to order the WMS layers with the zIndex property. The Geojson layer however is unable to fit between these too.
The Leaflet.Pane concept + a custom Pane was something I tried, this helps in moving the custom layers on top of lets say everything. But inside this custom pane, I can't assign an order to this Geojson (svg) layer.
In this example, the tileLayers and geoJSON are in the same custom pane. Inside this pane, I want to switch the order of the layers, base on a property (e.g. zIndex). But I can't find how to dos this with a TileLayer <> Geojson combination.
https://codesandbox.io/s/leaflet-custom-pane-fw0ue
Your issue is that you are trying to set the zIndex on the layers, instead of the panes they belong to. You need to set the zIndex on the pane itself. In vanilla leaflet:
map.createPane("top");
map.getPane("top").style.zIndex = "380";
map.createPane("middle");
map.getPane("middle").style.zIndex = "360";
map.createPane("bottom");
map.getPane("bottom").style.zIndex = "340";
var positron = L.tileLayer(
"https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png",
{
pane: "bottom",
}
).addTo(map);
var geojson = L.geoJson(euCountries, { pane: "middle"}).addTo(map);
var positronLabels = L.tileLayer(
"https://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}.png",
{
pane: "top"
}
).addTo(map);
Working codesandbox
This is a bit more elegant in react-leaflet, where you can wrap your various layers in Pane components:
const Map = (props) => {
return (
<MapContainer
doubleClickZoom={false}
id="mapId"
zoom={4}
center={[47.040182144806664, 9.667968750000002]}
>
<Pane name="top" style={{ zIndex: 380 }}>
<TileLayer
url="https://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}.png"
/>
</Pane>
<Pane name="middle" style={{ zIndex: 360 }}>
<GeoJSON data={countries} />
</Pane>
<Pane name="bottom" style={{ zIndex: 340 }}>
<TileLayer
url="https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png"
/>
</Pane>
</MapContainer>
);
};
Working codesandbox

React leaflet Marker position with latitude OR longitude indicator

I have been using react leaflet succesful with Int/Float coordinates like [51.505, -0.09]
however, i have longitude and Latitude indicator included on my coordinates like ['51.505W', '-0.09S'].
First options runs well with this code.
const position = [51.505, -0.09]
render(
<MapContainer center={position} zoom={13} scrollWheelZoom={false}>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Marker position={position}>
<Popup>
A pretty CSS3 popup. <br /> Easily customizable.
</Popup>
</Marker>
</MapContainer>
)
Thie second options breaks with error Invalid LatLng object How best can i handle this
Leaflet, and by extension react-leaflet, expects latitude longitude to simple numbers, either in the form of an array of 2 numbers [number, number], or an object in the format { lat: number, lng: number }. Coordinates like ['51.505W', '-0.09S'] don't fit that pattern. But they can be easily transformed with a simple function that strips the letter character from the string and returns a number:
function transformCoords(pair) {
return pair.map(coordString => {
return Number(
coordString
.replace('N', '')
.replace('S', '')
.replace('E', '')
.replace('W', '')
)
})
}
This will take in an array of the format ['51.505W', '-0.09S'] and will return an array like [51.505, -0.09], which can then be properly fed to a Marker position prop:
const position = ['51.505W', '-0.09S']
render(
<MapContainer ...>
<Marker position={transformCoords(position}) />
</MapContainer>
)

React-google-maps - MarkerWithLabel - Labels overlap

I'm trying to build a react project with the react-google-maps
library.
Basically it calculates the best route between 2 points and displays the route on the map using the library above.
For drawing I'm using the Polyline and MarkerWithLabel modules:
The Polyline to draw the segments
The Markers to place the start of a segment
The issue that I am having is the position of the labels form the MarkerWithLabel, if the zoom level is set to fit the whole route the labels overlap. This is because markers are very close and the zoom level is small, which is normal...
I've also attached 2 images
The first one with the zoom level to fit all the route (in which the markers label overlap)
The second one with the zoom level over one segment(in our case the city of Timisoara) to actual see the all the markers
The code for the MarkerWithLabel is below
const Markers = () => segments.map((segment) => {
const { marker } = segment;
const style = styles.vehicles[marker.vehicle.kind];
return (
<MarkerWithLabel
key={marker.key}
position={{ lat: marker.lat, lng: marker.lng }}
labelAnchor={new goo.Point(-10, 15)}
noRedraw
icon={{
path: goo.SymbolPath.CIRCLE,
scale: 4,
strokeWeight: 2,
fillOpacity: 1,
fillColor: '#ff3f10',
strokeColor: '#ffffff',
}}
labelClass="marker-label"
labelStyle={{
width: '400px',
backgroundColor: 'none',
color: style.color,
}}
>
<div style={{ clearFix: 'both' }}>
<span className="marker-text">
{marker.label}
</span>
<div className="circle">
{style.icon}
</div>
</div>
</MarkerWithLabel>
);
});
I want to know if there is a simple way of resolving the overlapping problem, just to place them somehow one next to the other or in different positions.
One solution would be to manually set the labelAnchor but I don't know how to find a solution for this, to detect which are overlapping and which not

How do you change the offset for a leaflet popup using angular leaflet directive and geojson?

I'm using the angular-leaflet-directive and geojson to create map markers using leaflet and mapbox. The popups on the markers aren't correctly aligned on the marker.
angular.extend($scope, { // Map data
geojson: {
data: $scope.filteredShows,
onEachFeature: function (feature, layer) {
layer.bindPopup(feature.properties.artist + ' · ' + feature.properties.venue);
layer.setIcon(defaultMarker);
layer.on({
mouseover: pointMouseover,
mouseout: pointMouseout
});
layers[feature.properties.id] = layer;
}
}
});
How do I change the offset on the markers?
Using popupAnchor: [-10, -10], in L.Icon. See http://leafletjs.com/reference.html#icon
If you're using the default images, but they're placed at a different location with different filenames because you're using a Rails server to serve the assets, for example, here's a tip so you don't have to hard code in the values from the default icon.
In my case, I injected the actual values into the proper location.
<script type="text/javascript">
var injectedData = {
paths: {
leafletIcon: {
iconRetinaUrl: '<%= image_url "leaflet-1.3.4/marker-icon-2x.png" %>',
iconUrl: '<%= image_url "leaflet-1.3.4/marker-icon.png" %>',
shadowUrl: '<%= image_url "leaflet-1.3.4/marker-shadow.png" %>',
},
},
};
</script>
Then, I created an instance of Icon that uses the default values for image offsets directly from the Icon.Default prototype.
import { Icon } from 'leaflet';
const defaultIcon = new Icon({
...Icon.Default.prototype.options,
...injectedData.paths.leafletIcon,
});
That's the same as injecting your data directly. Do as is appropriate for your particular use case.
const defaultIcon = new Icon({
...Icon.Default.prototype.options,
{
iconRetinaUrl: "/assets/leaflet-1.3.4/marker-icon-2x-00179c4c1ee830d3a108412ae0d294f55776cfeb085c60129a39aa6fc4ae2528.png",
iconUrl: "/assets/leaflet-1.3.4/marker-icon-574c3a5cca85f4114085b6841596d62f00d7c892c7b03f28cbfa301deb1dc437.png",
shadowUrl: "/assets/leaflet-1.3.4/marker-shadow-264f5c640339f042dd729062cfc04c17f8ea0f29882b538e3848ed8f10edb4da.png",
},
});
In my case, I was using the react-leaflet library with React, not Angular, but I'm sure you can adapt your use-case appropriately. In my case, I used the defaultIcon as a prop for the Marker component.
<Map center={position} zoom={zoom}>
<TileLayer
attribution='&copy OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Marker icon={defaultIcon} position={position}>
<Popup>
<span>{this.props.location}</span>
</Popup>
</Marker>
</Map>
I know this doesn't answer your question directly, but your question and vitalik_74's answer got me on the road to what I needed for my particular use-case, which was an easy but reliable way to provide different image URLs for the default icon set (including altered filenames) while also reusing the default offset numbers without having to hard code them in. I hope my answer can help someone else who comes across this question with this issue in the future.

leaflet.js disable duplicated world maps

We need to disable duplicated world maps at the left and right side of the main world map, which is showing by default.
Problem is that we need exact zoom level there and sometimes leaflet showing duplicates...
Is it possible to remove duplicates at all?
You will disable the noWrap property of your TiledLayer (which extends GridLayer). Documentation here
var layer = new L.tileLayer("http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
noWrap: true
});
If you use react-leafet you can set this prop in TileLayer component:
<MapContainer
center={{ lat: 51.505, lng: -0.09 }}
zoom={5}
style={mapStyle}
scrollWheelZoom={true}
>
<TileLayer
noWrap={true}
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
</MapContainer>