Simple Leaflet.js Marker Placement

2019-08-21 17:32发布

I'm using the open source Leaflet.js library to create a simple map.

I'm trying to solve a specific problem though. Markers are tied to a specific lat/lng on a map, which makes sense, but I need to be able to make a marker have a fixed offset position from another marker, without it being tied to a lat/lng center.

So for example, the map may look like this: a leaflet.js map of two markers

But when you zoom out, it'll look like this: enter image description here


What I actually want is for the right marker to be a fixed offset away from the left marker, instead of being tied to a latlng, as such: enter image description here


I've tried experimenting with unproject but I believe I'm going down the wrong path of how to handle this. It's unconventional what I'm doing but if anyone has any insight into how I could do this, it would be greatly appreciated.

2条回答
孤傲高冷的网名
2楼-- · 2019-08-21 18:30

This sounds like an XY problem to me: you want to display some extra info (possibly looking similar to a normal Marker) next to a Marker A (with some pixels offset), and you try with another Marker B; but Marker B is tied to Lat/Lng coordinates instead of pixels offset, so you ask for help on how to use unproject.

To achieve your original objective, a Leaflet DivIcon would indeed have been a more appropriate solution: a part of the <div> would contain your actual Marker Icon, and another part would contain your extra info. That way, it always stays at the desired position, without having to compute any (un)projection and without any JS event listener involved:

Screenshot map with Marker in DivIcon with extra info box

var paris = [48.86, 2.35];
var map = L.map('map').setView(paris, 11);

var divIcon = L.divIcon({
  html: `
    <img src="https://unpkg.com/leaflet@1.3.4/dist/images/marker-icon.png" />
    <div class="extra-info">
      Some extra info
    </div>
  `,
  className: 'my-div-icon',
  iconAnchor: [12, 41]
});

L.marker(paris, {
  icon: divIcon
}).addTo(map);

L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
html,
body,
#map {
  height: 100%;
  margin: 0;
}

.extra-info {
  position: absolute;
  left: 188px;
  bottom: -20px;
  min-width: 120px;
  background: yellow;
  border: 1px solid red;
}
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.4/dist/leaflet.css" integrity="sha512-puBpdR0798OZvTTbP4A8Ix/l+A4dHDD0DGqYW6RQ+9jxkRFclaxxQb/SJAWZfWAkuyeQUytO7+7N4QKrDh+drA==" crossorigin="" />
<script src="https://unpkg.com/leaflet@1.3.4/dist/leaflet-src.js" integrity="sha512-+ZaXMZ7sjFMiCigvm8WjllFy6g3aou3+GZngAtugLzrmPFKFK7yjSri0XnElvCTu/PrifAYQuxZTybAEkA8VOA==" crossorigin=""></script>

<div id="map"></div>

An even more appropriate solution would have been to use a Leaflet Tooltip, typically with your predefined offset, permanent: true option, a specific direction and custom styling (with className):

var paris = [48.86, 2.35];
var map = L.map('map').setView(paris, 11);

L.marker(paris).bindTooltip('Some extra info', {
  offset: [188, 0],
  className: 'my-tooltip',
  permanent: true,
  direction: 'right',
  interactive: true
}).addTo(map);

L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
html,
body,
#map {
  height: 100%;
  margin: 0;
}

.leaflet-tooltip.my-tooltip {
  background-color: yellow;
  border: 1px solid red;
  box-shadow: none;
}

.leaflet-tooltip.my-tooltip::before {
  content: none;
}
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.4/dist/leaflet.css" integrity="sha512-puBpdR0798OZvTTbP4A8Ix/l+A4dHDD0DGqYW6RQ+9jxkRFclaxxQb/SJAWZfWAkuyeQUytO7+7N4QKrDh+drA==" crossorigin="" />
<script src="https://unpkg.com/leaflet@1.3.4/dist/leaflet-src.js" integrity="sha512-+ZaXMZ7sjFMiCigvm8WjllFy6g3aou3+GZngAtugLzrmPFKFK7yjSri0XnElvCTu/PrifAYQuxZTybAEkA8VOA==" crossorigin=""></script>

<div id="map"></div>

Then if you really want your extra info to be styled and manipulated like a normal Leaflet Marker, another possible solution would be to use a custom Marker with modified iconAnchor, to account for your desired pixel offset:

var paris = [48.86, 2.35];
var map = L.map('map').setView(paris, 11);

var OffsetIcon = L.Icon.Default.extend({
  options: {
    // Subtract your desired offset.
    iconAnchor: [12 - 188, 41]
  }
});

L.marker(paris).addTo(map);
L.marker(paris, {
  icon: new OffsetIcon()
}).addTo(map);

L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
html,
body,
#map {
  height: 100%;
  margin: 0;
}
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.4/dist/leaflet.css" integrity="sha512-puBpdR0798OZvTTbP4A8Ix/l+A4dHDD0DGqYW6RQ+9jxkRFclaxxQb/SJAWZfWAkuyeQUytO7+7N4QKrDh+drA==" crossorigin="" />
<script src="https://unpkg.com/leaflet@1.3.4/dist/leaflet-src.js" integrity="sha512-+ZaXMZ7sjFMiCigvm8WjllFy6g3aou3+GZngAtugLzrmPFKFK7yjSri0XnElvCTu/PrifAYQuxZTybAEkA8VOA==" crossorigin=""></script>

<div id="map"></div>

查看更多
Root(大扎)
3楼-- · 2019-08-21 18:31

In addition to project() and unproject() methods to calculate position of second marker you can also listen to zoom events on the map and update the position of the second marker in order to keep the desired distance in pixels.

Have a look at the following example.

var marker;
var pos = L.latLng(28.478226,-16.313488);
var mymap = L.map('mapid').setView(pos, 13);
	
L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {
		maxZoom: 18,
		attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, ' +
			'<a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, ' +
			'Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
		id: 'mapbox.streets'
}).addTo(mymap);

L.marker(pos).addTo(mymap);
  
setLinkedMarkerAtDistance(180);
  
mymap.on('zoomstart', function() {
  if (marker) {
  	mymap.removeLayer(marker);
  }
});
  
mymap.on('zoomend', function() {
 	setLinkedMarkerAtDistance(180);
});

function setLinkedMarkerAtDistance(d) {
  var p = mymap.project(pos, mymap.getZoom());
  var p1 = p.add(L.point(d,0));
  var pos1 = mymap.unproject(p1, mymap.getZoom());
  if (marker) {
    marker.setLatLng(pos1).addTo(mymap);
  } else {
  	marker = L.marker(pos1).addTo(mymap);
  }
}
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.4/dist/leaflet.css" integrity="sha512-puBpdR0798OZvTTbP4A8Ix/l+A4dHDD0DGqYW6RQ+9jxkRFclaxxQb/SJAWZfWAkuyeQUytO7+7N4QKrDh+drA==" crossorigin=""/>
<script src="https://unpkg.com/leaflet@1.3.4/dist/leaflet.js" integrity="sha512-nMMmRyTVoLYqjP9hrbed9S+FzjZHW5gY1TWCHA5ckwXZBadntCNs8kEqAWdrb9O7rxbCaA4lKTIWjDXZxflOcA==" crossorigin=""></script>
<div id="mapid" style="width: 600px; height: 400px;">

I hope this helps!

查看更多
登录 后发表回答