我正在使用 Leaflet.js 和 Leaflet GPX 在地图上显示许多 GPX 轨迹。轨迹应该有白色阴影,以便它们在地图上更好地突出。为此,每个轨道都会添加到地图中两次,首先作为阴影(较宽的白色线),然后作为实际轨道(较细的线)。元素添加到地图的顺序实际上应该由Leaflet保留。
const trackColors = ['#FF6600', '#FF0066', '#6600FF', '#0066FF'];
// Create map
let map = L.map ('map');
// Create tile layer
L.tileLayer ('https://{s}.tile.openstreetmap.de/{z}/{x}/{y}.png').addTo (map);
// Add GPX tracks
for (let i = 0; i < tracks.length; i++)
{
// Create white track shadow, 8 pixels wide
let trackShadow = new L.GPX (tracks [i],
{
async: true,
marker_options:
{
startIconUrl: null,
endIconUrl: null,
shadowUrl: null
},
parseElements: ['track', 'route'],
polyline_options:
{
color: '#FFFFFF',
opacity: 0.6,
weight: 8,
lineCap: 'round'
}
}).addTo (map);
// Create colored track, 4 pixels wide
let track = new L.GPX (tracks [i],
{
async: true,
marker_options:
{
startIconUrl: null,
endIconUrl: null,
shadowUrl: null
},
parseElements: ['track', 'route'],
polyline_options:
{
color: trackColors [i % trackColors.length],
opacity: 1.0,
weight: 4,
lineCap: 'round'
}
}).addTo (map);
}
不幸的是,有时会发生轨道阴影绘制在实际轨道上的情况,这意味着元素会在 Leaflet 内自动重新排列。
bringToBack()
和 bringToFront()
手动重新排序所有轨道?由于您正在异步加载 *.gpx 文件,并且要执行两次(一次用于阴影,第二次用于原始轨道),因此您很可能会遇到竞争状况。这可以解释你问题的这一部分:
有时会发生轨道阴影绘制在实际轨道上的情况
您可以通过仅加载单个曲目一次,然后应用数据来解决该问题。例如,如下面的代码所示。请务必阅读评论。
const trackColors = ['#FF6600', '#FF0066', '#6600FF', '#0066FF'];
/* this is new - since I didn't have access to your original GPX files, I'm using these - found via Google search */
const tracks = [
'https://assets.codepen.io/2511086/Mendick+Hill-compressed.gpx',
'https://raw.githubusercontent.com/gps-touring/sample-gpx/master/BrittanyJura/Newhaven_Brighton.gpx',
'https://raw.githubusercontent.com/gps-touring/sample-gpx/master/BrittanyJura/Southampton_Portsmouth.gpx'
];
// Create map
let map = L.map ('map');
// Create tile layer
L.tileLayer ('https://{s}.tile.openstreetmap.de/{z}/{x}/{y}.png').addTo (map);
/* this is new - we need to load the gpx data asynchronously, and to wait for each of them before adding them to the map */
async function loadGPXData(url) {
try {
const response = await fetch(url);
if (!response.ok) {
// I'm throwing errors, but you can opt for a silent fail - you can return an empty string
// and check in the other function, where you're declaring const gpxData
// If it's an empty string, just continue. Or, you can both do that, and fill up
// an error string to be displayed in an alert, to inform the user that some
// of the tracks were not loaded to the map
throw new Error('Failed to fetch GPX data');
}
const gpxData = await response.text();
return gpxData;
} catch (error) {
// The same as above - you can return an empty string, or something prefaced with
// "error- <actual error>"
// or you can codify all of your responses to be JSONs with {"error":boolState,"data":data}
// structure, to have a consistent logic for processing it later on
console.error('Error loading GPX data:', error);
throw error;
}
}
/* this is new - we have to use the async function, since within it, we're awaiting the gpxData */
// Since the tracks are loaded asynchronously, and that could take a while, perhaps you could also
// inform your users with an overlaying DIV that loading is in progress, and then hide it once
// everything is done loading (either successfully or not)
// This isn't present in the sample code I provided, but it shouldn't be too difficult to implement
// You would just be showing and hiding the DIV
async function addGPXTracksToMap(tracks) {
for (let i = 0; i < tracks.length; i++) {
try {
const gpxData = await loadGPXData(tracks[i]);
/* this is new - instead of tracks[i], like in your original code, I'm using the retrieved data */
// Create white track shadow, 8 pixels wide
let trackShadow = new L.GPX(gpxData, {
parseElements: ['track', 'route'],
polyline_options: {
color: '#FFFFFF',
opacity: 0.6,
weight: 8,
lineCap: 'round'
}
}).addTo(map);
/* this is new - same as above, gpxData, instead of tracks[i]*/
// Create colored track, 4 pixels wide
let track = new L.GPX(gpxData, {
parseElements: ['track', 'route'],
polyline_options: {
color: trackColors[i % trackColors.length],
opacity: 1.0,
weight: 4,
lineCap: 'round'
}
}).addTo(map);
} catch (error) {
console.error('Error loading GPX data:', error);
}
}
}
addGPXTracksToMap(tracks);
map.setView([55.745, -3.384], 14);
#map {
height: 500px;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.css" integrity="sha512-h9FcoyWjHcOcmEVkxOfTLnmZFWIH0iZhZT1H2TbOq55xssQGEJHEaIm+PgoUaZbRvQTNTluNOEfb1ZRy6D3BOw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.js" integrity="sha512-puJW3E/qXDqYp9IfhAI54BJEaWIfloJ7JWs7OeD5i6ruC9JZL1gERT1wjtwXFlh7CjE7ZJ+/vcRZRkIYIb6p4g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-gpx/1.7.0/gpx.js" integrity="sha512-FatQYmF8k/KuvOgmwfRGRqlyzbG13etWz1GOO+JO6YQyhGgw5tVl9ihC9RS8S6iiS18CZAnZBvUoKHlGI6BTPQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<div id="map"></div>