丰图地图JS SDK实战:从零搭建一个物流轨迹展示页面

物流轨迹展示是物流类产品的标配功能。本文以丰图地图JS SDK为例,演示如何从零搭建一个可交互的物流车辆轨迹展示页面,涵盖地图初始化、轨迹绘制、标记点添加和轨迹回放等核心功能。

一、环境准备

1.1 获取AppKey

前往丰图开放平台(lbs.sfmap.com.cn)注册开发者账号,创建应用后获取ApiKey。

1.2 引入SDK

在HTML页面中通过script标签引入SDK:

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>物流轨迹展示</title>
    <style>
        #map-container {
            width: 100%;
            height: 600px;
        }
    </style>
</head>
<body>
    <div id="map-container"></div>
    <script src="https://lbs.sfmap.com.cn/sfmapsdk/map?ak=YOUR_APP_KEY"></script>
    <script>
        // SDK加载完成后执行
    </script>
</body>
</html>

对比其他平台

  • 丰图:https://lbs.sfmap.com.cn/sfmapsdk/map?ak=YOUR_KEY&v=3.1
  • 高德:https://webapi.amap.com/maps?v=2.0&key=YOUR_KEY
  • 百度:https://api.map.baidu.com/api?type=webgl&v=3.0&ak=YOUR_AK
  • 腾讯:https://map.qq.com/api/gljs?v=1.exp&key=YOUR_KEY

引入方式基本一致,都是script标签+ApiKey参数。

二、初始化地图

javascript 复制代码
// 丰图地图初始化
const map = new SFMap.Map('map-container', {
    center: [116.397428, 39.90923],  // [经度, 纬度]
    zoom: 12,
    ak: 'YOUR_APP_KEY'  // 支持 '2D' 和 '3D'
});

各平台初始化对比

功能 丰图 高德 百度 腾讯
初始化类 SFMap.Map AMap.Map BMapGL.Map TMap.Map
中心点格式 [lng, lat] [lng, lat] new Point(lng, lat) {lat, lng}
2D/3D切换 2D模式 viewMode 初始化时选择 自动
坐标系 GCJ-02 GCJ-02 BD-09 GCJ-02

注意坐标系差异:百度使用BD-09,其他三家使用GCJ-02。跨平台迁移时坐标转换是常见坑点。

三、绘制物流轨迹

假设从后端获取了一组GPS坐标点:

javascript 复制代码
// 物流轨迹数据([经度, 纬度]数组)
const trackPoints = [
    [116.397428, 39.90923],
    [116.407428, 39.91923],
    [116.417428, 39.92923],
    [116.427428, 39.93923],
    [116.437428, 39.94923]
];

// 创建线图层数据源
const lineSource = new SFMap.GeoJSONSource({
    type: 'FeatureCollection',
    features: [{
        type: 'Feature',
        geometry: {
            type: 'LineString',
            coordinates: trackPoints
        }
    }]
});

// 添加线图层
const lineLayer = new SFMap.FtLine({
    source: lineSource,
    layout: {
        'line-cap': 'round',
        'line-join': 'round'
    },
    paint: {
        'line-color': '#0066ff',
        'line-width': 4,
        'line-opacity': 0.8
    }
});

map.addLayer(lineLayer);

// 自动调整视野以完整显示轨迹
map.fitBounds(lineSource.getBounds());

各平台折线绘制API

平台 折线类 添加到地图
丰图 SFMap.FtLine map.addLayer()
高德 AMap.Polyline map.add()
百度 BMapGL.Polyline map.addOverlay()
腾讯 TMap.MultiPolyline 初始化时设置

丰图采用图层化架构(类似Mapbox),需要先创建数据源再添加图层;高德百度腾讯采用覆盖物模式,直接创建折线对象添加到地图。

四、添加起止点标记

javascript 复制代码
// 起点标记(绿色)
const startMarker = new SFMap.Marker({
    position: trackPoints[0],
    content: '<div style="background:#00cc66;color:white;padding:4px 8px;border-radius:4px;font-size:12px;">起点</div>',
    offset: new SFMap.Pixel(-20, -40)
});

// 终点标记(红色)
const endMarker = new SFMap.Marker({
    position: trackPoints[trackPoints.length - 1],
    content: '<div style="background:#ff3333;color:white;padding:4px 8px;border-radius:4px;font-size:12px;">终点</div>',
    offset: new SFMap.Pixel(-20, -40)
});

map.add([startMarker, endMarker]);

Marker的content属性支持自定义HTML,可以灵活定制标记样式。也可以使用icon属性指定图标URL。

五、轨迹回放功能

实现动态的轨迹回放效果:

javascript 复制代码
// 创建车辆标记
const truckMarker = new SFMap.Marker({
    position: trackPoints[0],
    content: '<div style="font-size:20px;">🚚</div>',
    offset: new SFMap.Pixel(-15, -30)
});
map.add(truckMarker);

// 回放控制
let currentIndex = 0;
let isPlaying = false;
let timer = null;

function startReplay() {
    if (isPlaying) return;
    isPlaying = true;
    currentIndex = 0;
    
    timer = setInterval(() => {
        if (currentIndex >= trackPoints.length) {
            stopReplay();
            return;
        }
        truckMarker.setPosition(trackPoints[currentIndex]);
        currentIndex++;
    }, 500);  // 每500ms移动一步
}

function stopReplay() {
    isPlaying = false;
    clearInterval(timer);
}

// 播放/暂停按钮
document.getElementById('play-btn').onclick = () => {
    if (isPlaying) {
        stopReplay();
        document.getElementById('play-btn').textContent = '播放';
    } else {
        startReplay();
        document.getElementById('play-btn').textContent = '暂停';
    }
};

六、完整代码

将以上代码组合,一个完整的物流轨迹展示页面:

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>物流轨迹展示Demo</title>
    <style>
        body { margin: 0; padding: 20px; font-family: sans-serif; }
        #map-container { width: 100%; height: 600px; border: 1px solid #ddd; }
        .controls { margin: 10px 0; }
        button { padding: 8px 16px; cursor: pointer; }
    </style>
</head>
<body>
    <h2>物流轨迹展示</h2>
    <div class="controls">
        <button id="play-btn">播放</button>
        <button id="reset-btn">重置</button>
    </div>
    <div id="map-container"></div>
    
    <script src="https://lbs.sfmap.com.cn/sfmapsdk/map?ak=YOUR_APP_KEY"></script>
    <script>
        // 轨迹数据
        const trackPoints = [
            [116.397428, 39.90923],
            [116.407428, 39.91923],
            [116.417428, 39.92923],
            [116.427428, 39.93923],
            [116.437428, 39.94923]
        ];
        
        // 初始化地图
        const map = new SFMap.Map('map-container', {
            center: [116.397428, 39.90923],
            zoom: 12,
            ak: 'YOUR_APP_KEY'
        });
        
        // 绘制轨迹(图层化方式)
        const lineSource = new SFMap.GeoJSONSource({
            type: 'FeatureCollection',
            features: [{
                type: 'Feature',
                geometry: {
                    type: 'LineString',
                    coordinates: trackPoints
                }
            }]
        });
        const lineLayer = new SFMap.FtLine({
            source: lineSource,
            paint: {
                'line-color': '#0066ff',
                'line-width': 4
            }
        });
        map.addLayer(lineLayer);
        
        // 起点终点标记
        const startMarker = new SFMap.Marker({
            position: trackPoints[0],
            content: '<div style="background:#00cc66;color:white;padding:4px 8px;border-radius:4px;">起点</div>'
        });
        const endMarker = new SFMap.Marker({
            position: trackPoints[trackPoints.length - 1],
            content: '<div style="background:#ff3333;color:white;padding:4px 8px;border-radius:4px;">终点</div>'
        });
        map.add([startMarker, endMarker]);
        
        // 车辆标记
        const truckMarker = new SFMap.Marker({
            position: trackPoints[0],
            content: '<div style="font-size:20px;">🚚</div>'
        });
        map.add(truckMarker);
        
        map.setFitView([polyline]);
        
        // 回放控制逻辑(同上)
        let currentIndex = 0;
        let isPlaying = false;
        let timer = null;
        
        document.getElementById('play-btn').onclick = function() {
            if (isPlaying) {
                isPlaying = false;
                clearInterval(timer);
                this.textContent = '播放';
            } else {
                isPlaying = true;
                this.textContent = '暂停';
                timer = setInterval(() => {
                    if (currentIndex >= trackPoints.length) {
                        isPlaying = false;
                        clearInterval(timer);
                        document.getElementById('play-btn').textContent = '播放';
                        return;
                    }
                    truckMarker.setPosition(trackPoints[currentIndex]);
                    currentIndex++;
                }, 500);
            }
        };
        
        document.getElementById('reset-btn').onclick = function() {
            stopReplay();
            currentIndex = 0;
            truckMarker.setPosition(trackPoints[0]);
            document.getElementById('play-btn').textContent = '播放';
        };
    </script>
</body>
</html>

这个Demo展示的是基础能力,但丰图在物流场景的几个差异化能力值得留意:

100个途经点的路径规划。高德支持16个,百度支持18个,丰图支持100个。对于配送路线优化、多站点调度这类场景,这个数字差异直接影响功能能不能做。

地址服务能力。智能地址填写、四级地址解析------物流场景里地址解析的准确率直接影响配送效率,丰图在这方面有比较深的积累。

价格。对于中小团队,丰图2万/年的项目版(纯授权证书)+按需搭配接口的模式,比5万/年的全家桶更容易接受。

从初始化地图到绘制轨迹、添加标记、实现回放,整个流程大概50行代码。如果你的产品核心场景是物流轨迹展示、地址管理这类需求,值得一试。