leaflet.js disable duplicated world maps - leaflet

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>

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

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

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>
)

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 to add custom UI to leaflet map?

I am using Leaflet to create a map game (very basic).
Basically I want to add an input <div> on the map so that when a user types in a location it will pan to a coordinate on the map.
I have tried creating elements and appending to the map <div> with variations of:
var d1 = document.getElementsByClassName('leaflet-control-container')[0];
d1.insertAdjacentHTML('afterbegin', '<div id="two">two</div>');
But the <div> is displayed behind the map and the image covers it.
How can I get it to show like the Zoom Control?
If I understand correctly, you would like to create your own "Control" (somehow visually similar to the Leaflet default Zoom Control, but with different functionality), that would allow looking for different locations and navigate to them.
As for styling a Control similar to Leaflet default ones (zoom, layers control), you need to:
Extend L.Control
Specify an onAdd method that returns the DOM element to be used as Control on the map. Steps 1 and 2 will make your Control add-able to a map corner as a standard Control, with proper z-index and margin.
Style it using your own class. To get a visual effect similar to the Zoom and Layers Controls, you can build on the leaflet-bar class:
.leaflet-bar {
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.65);
border-radius: 5px;
}
Example: (derived from the "Extending Leaflet: Handlers and Controls" tutorial)
var map = L.map('map').setView([48.86, 2.35], 11);
L.Control.MyControl = L.Control.extend({
onAdd: function(map) {
var el = L.DomUtil.create('div', 'leaflet-bar my-control');
el.innerHTML = 'My Control';
return el;
},
onRemove: function(map) {
// Nothing to do here
}
});
L.control.myControl = function(opts) {
return new L.Control.MyControl(opts);
}
L.control.myControl({
position: 'topright'
}).addTo(map);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
.my-control {
background: #fff;
padding: 5px;
}
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.3.0/dist/leaflet.css" integrity="sha512-Rksm5RenBEKSKFjgI3a41vrjkw4EVPlJ3+OiI65vTjIdo9brlAacEuKOiQ5OFh7cOI1bkDwLqdLw3Zg0cRJAAQ==" crossorigin="" />
<script src="https://unpkg.com/leaflet#1.3.0/dist/leaflet-src.js" integrity="sha512-2h9aokfcaYW7k0VPn1JqbQDQCaNQRrZJwetlnQ88yJrtIzGLVW/2StdQKoE+TIVNNTUxf6SVa+2vW2KB2EXnnA==" crossorigin=""></script>
<div id="map" style="height: 200px"></div>
That being said, the Control functionality that you would like to implement sounds very similar to that of the Leaflet Control Search plugin (aka "leaflet-search")
A Leaflet control that search markers/features location by custom property.

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.