【maptalks】POI跳转设计与实现


POI列表点击跳转几种交互形式:

A:点击后直接飞过去跳转缩放,这样的效果缺点是在多个POI中跳转频繁时看着比较眼花缭乱;(eg.小红书地图)
B:点击后放大POI,假如点位超出范围,则地图平移到以该点位为中心的位置。(eg.高德地图)
C:点击POI后弹窗,锁定地图范围并去除其他POI(eg.其他应用)

方式一

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>MapZoomToPOI</title>
		<script src="https://unpkg.com/maptalks@0.47.5/dist/maptalks.min.js"></script>
		<style>
			#map {
				width: 100%;
				height: 500px;
			}
		</style>
	</head>
	<body>

		<div id="map"></div>
		<button id="zoomButton">Zoom to POI</button>

		<script>
			
			var poi = [-74.08087539941407, 40.636167734187026]; // POI的位置
			// 创建maptalks地图对象
			var map = new maptalks.Map('map', {
				center: [-74.08087539941407, 40.636167734187026], // 初始地图中心
				zoom: 10, // 初始缩放级别
				pitch: 0, // 初始俯仰角
				bearing: 0, // 初始航向角
				baseLayer: new maptalks.TileLayer('base', {
					urlTemplate: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
					subdomains: ['a', 'b', 'c'],
					attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
				})
			}); 
			var layer = new maptalks.VectorLayer('v').addTo(map);
			layer.addTo(map);
			var marker = new maptalks.Marker(poi, {
				symbol: {
					'markerType': 'ellipse', // marker形状
					'markerFill': '#FF5733', // marker填充颜色
					'markerWidth': 20, // marker宽度
					'markerHeight': 20, // marker高度
				}
			}).addTo(layer);
			// 点击按钮时触发的缩放到POI的动画
			document.getElementById('zoomButton').addEventListener('click', function() {
				
				map.animateTo({
					center: poi, // POI位置
					zoom: 13, // 缩放级别
					pitch: 0, // 俯仰角
					bearing: 20 // 航向角
				}, {
					duration: 2000 // 动画持续时间,单位毫秒
				});
			});
		</script>

	</body>
</html>

方式二

在这里插入图片描述
在这里插入图片描述

这里用到turf工具:https://fenxianglu.cn/turfjs/category/

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Map with POI</title>
  <link rel="stylesheet" href="https://unpkg.com/maptalks/dist/maptalks.css">
  <script src="https://unpkg.com/maptalks/dist/maptalks.min.js"></script>
  <script src="https://unpkg.com/@turf/turf@6/turf.min.js"></script> <!-- 引入 turf -->
  <style>
    body, html {
      margin: 0;
      height: 100%;
      width: 100%;
    }
    .container {
      display: flex;
      flex-direction: column;
      height: 100%;
    }
    #map {
      width: 100%;
      height: 80%;
    }
    .poi-list {
      background: #f0f0f0;
      padding: 10px;
      height: 20%;
      overflow-y: scroll;
    }
    .poi-list ul {
      list-style-type: none;
      padding: 0;
    }
    .poi-list li {
      padding: 8px;
      cursor: pointer;
    }
    .poi-list li:hover {
      background-color: #ddd;
    }
  </style>
</head>
<body>

<div class="container">
  <div id="map"></div>
  <div class="poi-list">
    <ul>
      <li onclick="onPoiClick({ name: 'POI 1', coordinates: [-0.131049, 51.498568] })">POI 1</li>
      <li onclick="onPoiClick({ name: 'POI 2', coordinates: [-0.107049, 51.498568] })">POI 2</li>
      <li onclick="onPoiClick({ name: 'POI 3', coordinates: [-0.107049, 51.491568] })">POI 3</li>
    </ul>
  </div>
</div>

<script>
  let map;
  let poiLayer; // 新增一个Layer来管理POI标记
  let poiMarkers = [];
  let pickedPointSymbol = null;
  let lastPickedMarker = null; // 存储上一个选中的 marker
let lastPickedMarkerAni= null; // 存储上一个选中的 marker的动画对象
  function initializeMap() {
    map = new maptalks.Map('map', {
      center: [-0.113049, 51.498568], // 初始中心
      zoom: 14,
      baseLayer: new maptalks.TileLayer('base', {
        urlTemplate: 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png',
        subdomains: ['a', 'b', 'c', 'd'],
        attribution: '&copy; <a href="http://osm.org">OpenStreetMap</a> contributors, &copy; <a href="https://carto.com/">CARTO</a>'
      })
    });

    // 创建一个新的 VectorLayer 来管理 POI 标记
    poiLayer = new maptalks.VectorLayer('poiLayer').addTo(map);

    const pois = [
      { name: 'POI 1', coordinates: [-0.131049, 51.498568] },
      { name: 'POI 2', coordinates: [-0.107049, 51.498568] },
      { name: 'POI 3', coordinates: [-0.107049, 51.491568] },
    ];

    pois.forEach(poi => {
      const marker = new maptalks.Marker(poi.coordinates, {
        symbol: {
          'markerType': 'ellipse',
          'markerWidth': 20,
          'markerHeight': 20,
          'markerFill': '#FF4500',
          'markerOpacity': 0.7,
        }
      });

      // 将标记添加到 POI 图层
      poiLayer.addGeometry(marker);
      poiMarkers.push({ poi, marker });

      // 添加点击事件监听器
      marker.on('click', function(e) {
        onMarkerClick(e.target);
      });
    });
  }

  // POI 列表点击事件
  function onPoiClick(poi) {
    const markerData = poiMarkers.find(item => item.poi.name === poi.name);
    if (markerData) {
      onMarkerClick(markerData.marker); // 触发对应的 Marker 点击事件
    }
  }

  // POI 点击事件处理
  function onMarkerClick(marker) {
    // 凸显该POI
      highlightPoi(marker);
	  // 判断POI是否在视野范围内
    if (!isPoiInView(marker)) {
      // 如果不在视野范围内,平移地图并聚焦该POI
      centerMapOnPoi(marker);
    }
  }

  // 判断POI是否在地图的视野范围内
  function isPoiInView(marker) {
    const extent = map.getExtent(); // 获取地图的视野范围
    console.log('Map extent:', extent);
    const { xmax, ymax, xmin, ymin } = extent; // (xmin, ymin) 为左下角坐标,(xmax, ymax) 为右上角坐标

    // 使用 turf 判断POI是否在视野范围内
    const { x, y } = marker.getCoordinates();
    const point = turf.point([x, y]);
    const polygonCoordinates = [
      [
        [xmin, ymin],
        [xmax, ymin],
        [xmax, ymax],
        [xmin, ymax],
        [xmin, ymin],
      ],
    ];
    const searchWithin = turf.polygon(polygonCoordinates);

    // 使用 booleanPointInPolygon 判断点是否在多边形内
    const isInPolygon = turf.booleanPointInPolygon(point, searchWithin);
    return isInPolygon;
  }

  // 凸显POI的标记,并使用动画
  function highlightPoi(marker) {
    // 如果之前有凸显的POI标记,恢复它的原始尺寸
    if (lastPickedMarker) {
	  lastPickedMarkerAni.finish();//注意 用finish而非cancel
      lastPickedMarker.updateSymbol(pickedPointSymbol);
    }
    // 获取当前标记的原始尺寸
    pickedPointSymbol = marker.getSymbol();

    // 设置当前标记为凸显状态(仅修改 markerWidth 和 markerHeight),并使用动画

    var Ani=marker.animate({
      symbol: {
        markerWidth: 30,
        markerHeight: 30
      }
    }, {
      duration: 100
    });
    
    // 保存当前选中的 marker和动画对象
    lastPickedMarker = marker;
	lastPickedMarkerAni = Ani;
  }

  // 平移地图并聚焦POI
  function centerMapOnPoi(marker) {
    const center = new maptalks.Coordinate(marker.getCoordinates());
    console.log(`Panning to POI at ${marker.getCoordinates()}`);
    map.panTo(center); // 只平移不缩放
  }

  // 初始化地图
  initializeMap();
</script>

</body>
</html>

方式三

在这里插入图片描述
在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>MapZoomToPOI</title>
    <script src="https://unpkg.com/maptalks@0.47.5/dist/maptalks.min.js"></script>
    <style>
        #map {
            width: 100%;
            height: 500px;
        }
    </style>
</head>
<body>

<div id="map"></div>
<button id="zoomButton">Zoom to POI</button>

<script>
    var poi = [-74.08087539941407, 40.636167734187026]; // POI的位置
    var nearbyPoi1 = [-74.079, 40.637]; // 第一个附近点
    var nearbyPoi2 = [-74.082, 40.635]; // 第二个附近点

    // 创建maptalks地图对象
    var map = new maptalks.Map('map', {
        center: poi, // 初始地图中心
        zoom: 10, // 初始缩放级别
        pitch: 0, // 初始俯仰角
        bearing: 0, // 初始航向角
        baseLayer: new maptalks.TileLayer('base', {
            urlTemplate: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
            subdomains: ['a', 'b', 'c'],
            attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
        })
    });

    var layer = new maptalks.VectorLayer('v').addTo(map);

    // 创建POI标记
    var markerPoi = new maptalks.Marker(poi, {
        symbol: {
            'markerType': 'ellipse', // marker形状
            'markerFill': '#FF5733', // marker填充颜色
            'markerWidth': 20, // marker宽度
            'markerHeight': 20, // marker高度
        },
        properties: {
            title: "POI地点", // 标题
            dis: 1.2, // 距离
            img: "<img src='https://via.placeholder.com/100' />", // 图片
            star: 4.5 // 评分
        }
    }).addTo(layer);

    // 创建附近点的标记
    var markerNearby1 = new maptalks.Marker(nearbyPoi1, {
        symbol: {
            'markerType': 'ellipse',
            'markerFill': '#33FF57', // 不同的颜色
            'markerWidth': 20,
            'markerHeight': 20,
        }
    }).addTo(layer);

    var markerNearby2 = new maptalks.Marker(nearbyPoi2, {
        symbol: {
            'markerType': 'ellipse',
            'markerFill': '#3357FF', // 不同的颜色
            'markerWidth': 20,
            'markerHeight': 20,
        }
    }).addTo(layer);

    // 设置信息框
    markerPoi.setInfoWindow({
        title: markerPoi.properties.title,
        content: '<br style="color:#f00">距离你 ' + markerPoi.properties.dis + ' km ' + markerPoi.properties.img + '</br>评分: ' + markerPoi.properties.star
    });

    // 鼠标交互事件监听
    markerPoi
        .on("mouseenter", function (e) {
            e.target.updateSymbol({
                markerFill: "#f00", // 鼠标进入时改变标记颜色
            });
        })
        .on("mouseout", function (e) {
            e.target.updateSymbol({
                markerFill: "#FF5733", // 鼠标离开时恢复标记颜色
            });
        })
        .on("click", function (e) {
            // 打开信息窗口
            e.target.openInfoWindow(e.coordinate);
        });

    // 点击按钮时触发的缩放到POI的动画
    document.getElementById('zoomButton').addEventListener('click', function() {
        // 隐藏其他两个标记
        markerNearby1.remove();
        markerNearby2.remove();

        // 执行地图动画,缩放到POI位置
        map.animateTo({
            center: poi, // POI位置
            zoom: 13, // 缩放级别
            pitch: 0, // 俯仰角
            bearing: 20 // 航向角
        }, {
            duration: 1000 // 动画持续时间,单位毫秒
        });
    });

    // 在动画完成后禁用交互功能
    map.on('animateend', function () {
        // 禁用交互功能
        map.disableZoom(); // 禁用缩放
        map.disableDrag(); // 禁用拖动
        map.disablePitch(); // 禁用俯仰角
        map.disableRotation(); // 禁用旋转
    });
</script>

</body>
</html>

其中值得一提的是,如果不使用disable方法,而是用修改map参数的方法,则需要考虑到移动端和web端有两套参数需要同步修改,需要注意。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

灵境引路人

感谢投喂 ~ ❤

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值