前言

日常生活中,常常面临在多个相似目的地之间选择,往往真正缺乏的不是路径规划,而是对“哪个更符合需求”+“哪个最近”的清晰认识。解决这一问题其实并不需要详细了解每个目的地的具体距离,而只需将这些同类POI和起点坐标标记在地图上,便能直观地识别出哪个离我们最近。结合高德地图与LBS(位置服务)思想,本文将以钓鱼平台为例,探讨如何基于组合需求,实现对目的地的有效路径规划,帮助用户快速做出决策。

背景

   作为钓鱼爱好者,一直希望有一个平台能帮用户找到合适的钓鱼地点、记录和分享钓鱼成果。此外,如何找到一个“最近”的鱼塘“既能钓鲫鱼又能钓鲢鱼”这样的需求便成为了本项目的初衷。

技术架构

平台采用前后端分离的架构,主要组成部分如下:

前端:使用HTML、CSS和JS构建用户界面,并通过高德地图API展示地图和钓鱼点信息。

后端:基于Java Servlet技术,处理用户请求,连接PostgreSQL数据库。

数据库:使用PostgreSQL便于储存空间数据点,管理用户上传的钓鱼信息和记录。

方案设计

输入

用户通过前端界面上传钓鱼信息,包括鱼类名称、钓鱼地点及时间。

这些信息被发送到后端,通过相应的Servlet处理,并存储到数据库中。

输出

用户请求时,后端从数据库中检索钓鱼点信息,并将结果返回前端。

例如,通过GetFishInfoServlet.java,系统获取并返回钓鱼记录数据,供用户在地图上查看。

<pre class="ag" style="font-family:monospace;">try (Connection connection = Database.getConnection()) {

            String sql = &quot;SELECT l.location_id, f.fish_name, l.latitude, l.longitude, l.province, l.city, l.district &quot; +

                    &quot;FROM locations l &quot; +

                    &quot;JOIN fishing_records fr ON l.location_id = fr.location_id &quot; +

                    &quot;JOIN fish f ON fr.fish_id = f.fish_id&quot;;

            Statement stmt = connection.createStatement();

            ResultSet rs = stmt.executeQuery(sql);

            while (rs.next()) {

                int locationId = rs.getInt(&quot;location_id&quot;);

                String fishName = rs.getString(&quot;fish_name&quot;);

                double latitude = rs.getDouble(&quot;latitude&quot;);

                double longitude = rs.getDouble(&quot;longitude&quot;);

                String province = rs.getString(&quot;province&quot;);

                String city = rs.getString(&quot;city&quot;);

                String district = rs.getString(&quot;district&quot;);

                FishInfo fishInfo = new FishInfo(locationId, fishName, latitude, longitude, province, city, district);

                fishList.add(fishInfo);

            }

            rs.close();

            stmt.close();

        } catch (SQLException e) {

            e.printStackTrace();

        }

&nbsp;

&nbsp;</pre>

高德API的使用

在钓鱼指南平台中,高德地图API主要用于:

1.地图初始化与设置

var map;

var driving;

var Center = localStorage.getItem('mapCenter') ? JSON.parse(localStorage.getItem('mapCenter')) : [114.361084, 30.533393];

var Zoom = localStorage.getItem('mapZoom') ? parseInt(localStorage.getItem('mapZoom')) : 12;

function initMap() {

    map = new AMap.Map('mapContainer', {

        zoom: Zoom,

        center: Center,

        mapStyle: 'amap://styles/fresh'

    });

    driving = new AMap.Driving({

        map: map,

        panel: "panel"

    });

    // 地图点击事件,用于标记钓鱼点

    map.on('click', function (e) {

        var lat = e.lnglat.getLat();

        var lng = e.lnglat.getLng();

        document.getElementById('latitude').value = lat;

        document.getElementById('longitude').value = lng;

        if (marker) {

            marker.setMap(null);

        }

        marker = new AMap.Marker({

            position: [lng, lat],

            map: map,

            icon: blueIcon,

            offset: new AMap.Pixel(-21, -43)

        });

    });

}

在这段代码中,首先声明了 map 和 driving 变量,分别用于存储地图实例和路径规划实例。通过 localStorage 获取地图的初始中心和缩放级别,如果没有保存的状态则使用默认值。initMap 函数负责创建地图实例,并设置缩放、中心位置和样式。

添加地图的点击事件以便用户可以在地图上选择位置。当用户点击地图时,事件处理程序会获取点击位置的经纬度,并在该位置放置一个标记。这样用户可以直观地看到自己选择的位置。

2.添加钓鱼点标记

function addMarker(lat, lng, fishInfo) {

    var marker = new AMap.Marker({

        position: [lng, lat],

        map: map,

        icon: redIcon,

        offset: new AMap.Pixel(-17, -25)

    });

    marker.content = fishInfo;

    marker.on('click', function (e) {

        infoWindow = new AMap.InfoWindow({

            content: e.target.content,

            offset: new AMap.Pixel(0, -30)

        });

        infoWindow.open(map, e.target.getPosition());

    });

}

addMarker 函数用于在地图上添加钓鱼点的标记。通过传入纬度、经度和钓鱼点信息,该函数创建一个新的标记,并设置其位置和图标。标记上还附加了钓鱼点的信息内容,以便在用户点击标记时能够显示详细信息。

点击标记时,事件处理程序会创建一个信息窗口并显示相关内容,让用户能够快速获取该钓鱼点的详细信息。这种设计使得用户能够与地图进行互动,获取更丰富的上下文信息。

3.页面加载时获取钓鱼点信息

window.onload = function () {

    initMap();

    // 从服务器获取钓鱼点信息并添加到地图

    fetch('GetFishInfoServlet')

        .then(response => response.json())

        .then(data => {

            data.forEach(fish => {

                var con =

                    "<div>"+

                    "<h3>"+fish.fishName+"</h3>"+

                    "<p>省份:"+fish.province+"</p>"+

                    "<p>城市: "+fish.city+"</p>"+

                    "<p>区县: "+fish.district+"</p>"+

                    "<p>纬度: "+fish.latitude+"</p>"+

                    "<p>经度: "+fish.longitude+"</p>"+

                    "</div>"

                ;

                addMarker(fish.latitude, fish.longitude, con);

            });

        })

        .catch(error => console.error('Error:', error));

}

在页面加载时,window.onload 函数首先调用 initMap 来初始化地图。接下来,通过 fetch 请求 GetFishInfoServlet 接口从服务器获取钓鱼点的信息。获取的数据会以JSON格式返回,然后通过遍历这些数据,使用 addMarker 函数将每个钓鱼点在地图上标记出来。

每个钓鱼点的信息被构建为一个HTML片段,包含鱼类名称、所在省份、城市、区县以及其经纬度等。这种方式让用户在地图上能够快速识别钓鱼点及其相关信息,提升了用户体验。

4. 清除标记与存储处理

function clearMarkers() {

    map.clearMap();

}

function clearLocalStorage() {

    localStorage.removeItem('mapCenter');

    localStorage.removeItem('mapZoom');

    alert('本地存储已清除。地图将在下次加载时重置为默认位置。');

}

clearMarkers 函数用于清除地图上的所有标记,使地图恢复到初始状态。这在用户需要重新选择位置或查看不同钓鱼点时非常有用。

clearLocalStorage 函数则用于清除本地存储中的地图状态,使得下次加载时地图能够重置为默认位置。同时会弹出提示,告知用户本地存储已被清除,增强了用户的操作反馈。

5.路径规划

当然,还有最重要的路径规划部分。

首先初始化路径规划实例:

driving = new AMap.Driving({

    map: map,

    panel: "panel"

});

在 initMap 函数中,driving 变量被初始化为一个高德地图的驾车路径规划实例。此实例通过将其与地图实例 (map) 和用于展示导航信息的面板 (panel) 关联,实现路径规划功能。这意味着用户可以通过该实例请求从起点到终点的导航。

function startNavigation() {

    var fishName = document.getElementById('search_fish_name').value;

    var lat = document.getElementById('latitude').value;

    var lng = document.getElementById('longitude').value;

    if (!fishName || !lat || !lng) {

        alert('请点击地图以选择坐标并输入鱼名');

        return;

    }

    fetch('SearchFishInfoServlet?search_fish_name=' + fishName + '&latitude=' + lat + '&longitude=' + lng + "&kind=" + 1)

        .then(response => response.json())

        .then(data => {

            clearMarkers();

            var marker = new AMap.Marker({

                position: [lng, lat],

                map: map,

                icon: blueIcon,

                offset: new AMap.Pixel(-21, -43)

            });

            data.forEach(fish => {

                addMarker(fish.latitude, fish.longitude, fish);

                driving.search(new AMap.LngLat(lng, lat), new AMap.LngLat(fish.longitude, fish.latitude), function (status, result) {

                    if (status === 'complete') {

                        console.log('路线绘制完成');

                    } else {

                        console.error('获取驾车数据失败:', result);

                    }

                });

            });

        })

        .catch(error => console.error('Error:', error));

}

定义开始导航功能,在 startNavigation 函数中,用户输入的鱼类名称、纬度和经度会被提取。如果这些信息不完整,系统会提醒用户进行必要的输入。接下来,程序向 SearchFishInfoServlet 发送请求,查找与该鱼类相关的钓鱼点。

一旦获取到相关钓鱼点的信息,程序会首先清除之前的标记,然后在用户选定的位置添加一个标记。在获取的钓鱼点数据中,调用 driving.search 方法进行路径规划。

driving.search(new AMap.LngLat(lng, lat), new AMap.LngLat(fish.longitude, fish.latitude), function (status, result) {

    if (status === 'complete') {

        console.log('路线绘制完成');

    } else {

        console.error('获取驾车数据失败:', result);

    }

});

driving.search 方法接收起始点(用户当前位置)和目标点(钓鱼点)的经纬度,并在回调函数中处理结果。如果路径规划成功,控制台将显示“路线绘制完成”;如果失败,则输出错误信息。这一过程实现了用户从当前位置到钓鱼点的导航功能。

function stopNavigation() {

    driving.clear(); // Clear the navigation

    console.log('导航已停止');

}

stopNavigation 函数用于停止当前的导航操作,通过调用 driving.clear() 清除正在进行的导航路线,并在控制台记录停止的事件。这一功能使得用户可以在需要时随时中断导航,返回到地图的正常浏览状态。

效果展示

图中的POI都是用户点击上传的钓鱼点,可以看到,页面的四个角一共有五个按钮,对应了不同功能。点击右上角的“搜索鱼类信息”按钮,会弹出进一步的功能窗,该部分主要实现向用户推荐钓鱼点、搜索查询、导航的功能。

获取用户起点:

用户可以基于多种需求组合筛选目的地,系统选择最近目的地,就可开始导航。高德api服务可以方便地展示路径规划和地图路线。当然,也可以视用户需求添加步行、骑车的路径规划实例。

结语

用户的需求日益多样化,尤其是在多个相似目的地中选择时,怎样结合自身需求最快实现路径规划?本文通过LBS(位置服务)思想,我们能够将用户的地理位置与丰富的兴趣点(POI)数据结合起来,为他们提供更加精准、便捷的服务。在这个背景下,本文不仅仅旨在开发钓鱼指南平台,而是探索了一种全新的信息获取方式,向受众范围更加明确的用户提供专业平台,使他们能够轻松找到最近的目标。

不仅仅是“哪个最近的鱼塘既能钓鲫鱼又能钓鲢鱼?”的问题,也可以是“哪个超市最近且同时有新鲜水果和烘焙面包?”“哪个最近的餐厅既能提供海鲜又有素食菜肴?”“哪里可以找到最近的咖啡店,同时提供无线网络和桌位?”“最近的健身房中,哪里有瑜伽课程和力量训练设施?”……在用户没下定主意之前,平台能否根据用户需求提供最近POI的路径规划?

本文只是抛砖引玉,展示了高德api+LBS的广泛应用潜力。作为开发者,可以根据个人兴趣和需求设计出更多具有实用价值的细化服务平台,满足用户在不同场景下的需求,未来这种智能化的服务方式必将覆盖更多生活场景,推动社会资源的合理配置与高效利用,开启更加智能的生活方式。

参考文档

参考手册-地图 JS API 2.0|高德地图API (amap.com)

Logo

「智能机器人开发者大赛」官方平台,致力于为开发者和参赛选手提供赛事技术指导、行业标准解读及团队实战案例解析;聚焦智能机器人开发全栈技术闭环,助力开发者攻克技术瓶颈,促进软硬件集成、场景应用及商业化落地的深度研讨。 加入智能机器人开发者社区iRobot Developer,与全球极客并肩突破技术边界,定义机器人开发的未来范式!

更多推荐