使用Leaflet.js和Leaflet GPX绘制带阴影的GPX轨迹

问题描述 投票:0回答:1

我正在使用 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()
    手动重新排序所有轨道?
javascript leaflet gpx
1个回答
0
投票

由于您正在异步加载 *.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>

© www.soinside.com 2019 - 2024. All rights reserved.