【Uniapp】高德地图的接入、定位、自定义标点与信息窗体使用

一、前言

因为公司的业务需求,需要实现一个接入高德地图的数据大屏,并根据坐标实现地图标点渲染,自定义信息窗体,点击定位等功能。查阅高德地图官方文档时发现使用的是原生 JavaScript ,且网上 uniapp 接入使用的教程较少,我自己摸索之后解决了不少问题,欢迎大佬补充纠正。

📌 更新了一篇 腾讯地图 的博客,并附有demo,大家可以根据需求来选用:地址

二、准备工作

1、参考博客以及相关地址

高德开发平台:平台地址

注册参考博客:博客地址

2、注册高德开发平台账号

我们需要先在高德开放平台注册一个账号,以申请获取 key 等权限信息。注册流程很简单,就不赘述了,获取成功后点击此处:

image-20230504100153524

点击创建新应用,至于里面的内容就是随便填都可以:

image-20230504100638922

点击添加 Key ,创建你的 Key 值,我们后面都需要使用,具体你是哪个开发平台就勾选哪一个:

image-20230504100932468

下面是最后生成的 Key。在参考博客中是直接使用 key 获取并本地导入下载的SDK,而我使用的是动态导入,大家可以尝试一下哪个更适合你的开发。

image-20230504101121345

三、导入以及使用

以下内容较多篇幅较长,大家可以根据目录选择阅读,我会结合官方文档以及参考文档详细说明每一个方法的使用以及具体实现,但最好还是按顺序看下去。

1、参考博客与文档以及相关地址

高德官方文档:地址

开发参考博客:地址(不是我的博客)

  • 实际上该博客相当于官方文档的导读摘要,大家可以看看哪些是你需要的,去官方文档再详细看就好了

2、导入SDK

在导入这一块我尝试了很多种方法,最终实现的是这个方法:怎么引入高德地图 ,通过这个方法可以定义一个全局的地图实例供我们调用高德API。

效果

image-20230504142312187

代码
<template>
	<!-- 地图底框 -->
	<view id="wrapper"></view>
</template>
<script>
    let mapObj = null; // 生成地图实例
    window.mapInit = function() {
		mapObj = new AMap.Map('wrapper', {
			center: [113.382, 22.5211],
			layers: [
				new AMap.TileLayer.Satellite(), // 卫星
				new AMap.TileLayer.RoadNet() // 路网
			],
			zoom: 13
		});
		// 在这里可以做一些回调处理  定位 标记点 等等  或者把amap弄成一个全局变量
		// 引入单个插件
		AMap.plugin('AMap.ToolBar', function() { // 异步加载插件
			var toolbar = new AMap.ToolBar();
			map.addControl(toolbar);
		});
	};
    export default {
        data() {}onLoad() {
			// #ifdef H5
			this.loadScrpit();
			// #endif
		},
         methods: { 
            loadScrpit() { // 挂载动态js
				var script = document.createElement('script');
				script.src = 'https://webapi.amap.com/maps?v=1.4.15&key=(这里是你申请的KEY)&callback=mapInit';
				document.body.appendChild(script);
			},
         }
    }
</script>
代码讲解

首先是在 onLoad() 在页面加载阶段内调用动态挂载函数,熟悉原生 JavaScript 的朋友都应该可以明白这是一个创建 <script></script> 标签,并把它插入 body 内的操作。关键是在标签的属性上,我们需要传入 Key 值和回调函数。

📌 此处的回调函数可以取任意名,但需要注意的是在 window.function() 接收函数时需要同名,否则无法成功接收回调函数。

mapObj = new AMap.Map('wrapper', {
	center: [Longitude, Latitude],
	layers: [
		new AMap.TileLayer.Satellite(), // 卫星
		new AMap.TileLayer.RoadNet() // 路网
	],
	zoom: 13
});

在本文中我使用 mapObj 全局存储地图的实例, new AMap.Map('id',Object) 的参数:

  • id:地图将会渲染在同名 id 的 view 或 div 内。

    📍 需要注意的是,如果你想要在地图上自定义一些控件或展示框,最好不在该标签内添加,因为当你定位到一个新的地点时,它将刷新整个地图,你所定义的界面将会全部消失。正确的做法应该是建立一个同级的标签,通过 z-index 属性置于地图的上层。

    <template>
    	<view>
        	<!-- 地图底框 -->
    		<view id="wrapper"></view>
    		<!-- 你的界面 -->
    		<view id="box"></view>
        </view>
    </template>
    
  • Object:你可以通过该对象决定你的地图需要哪些图层与初始定位,还有缩放的级数。

    • center:[ Longitude , Latitude ] ,设置中心点的位置;
    • layers:根据业务需求决定需要什么图层,常用应该只有卫星和路网这两种;
    • zoom:初始地图的缩放级数,同样也是根据你的业务需求设置就好了。

3、点击坐标定位

点击代码实际上和挂载地图是一样的代码,只需要修改中心点的位置,重新挂载即可。但问题也随之出现,如果你已经设置了标点坐标与信息窗体,重新挂载后将会消除这些信息,你需要再次挂载这些信息。也就是说你如果需要保存定位前的标点,需要先保存对应的坐标。

代码
mapObj = new AMap.Map('wrapper', {
	center: [Longitude, Latitude],
	layers: [
		new AMap.TileLayer.Satellite(), // 卫星
		new AMap.TileLayer.RoadNet() // 路网
	],
	zoom: 13
});
代码讲解

基本和上面说的一样,只要替换中心的定位坐标即可。在下面你会了解到如何挂载自定义标点与信息窗体,这些信息都会在你定位后消失,需要重新挂载,也就是保存坐标信息再次挂载坐标标点。

4、自定义标点

自定义标点主要是定义图标的图像以及大小、偏移量等参数。需要注意的是,标点自定义必须在地图实例挂载之后,否则无法定义成功。自定义标点信息成功后再挂载标点,最后实现的就是下图效果。

效果

image-20230612101134611

代码
<template>
	<!-- 地图底框 -->
	<view id="wrapper"></view>
</template>
<script>
    let mapObj = null; // 生成地图实例
    // ------------------------------ 更新内容1 ----------------------------- //
    let icon = null; // 生成图片实例
	let punctuation = []; // 生成标点实例
    // ---------------------------------------------------------------------- //
    window.mapInit = function() {
		mapObj = new AMap.Map('wrapper', {
			center: [Longitude, Latitude],
			layers: [
				new AMap.TileLayer.Satellite(), // 卫星
				new AMap.TileLayer.RoadNet() // 路网
			],
			zoom: 13
		});
		// 在这里可以做一些回调处理  定位 标记点 等等  或者把amap弄成一个全局变量
		// 引入单个插件
		AMap.plugin('AMap.ToolBar', function() { // 异步加载插件
			var toolbar = new AMap.ToolBar();
			map.addControl(toolbar);
		});
	};
    export default {
        data() {}onLoad() {
			// #ifdef H5
			this.loadScrpit();
			// #endif
         	// ------------------------------ 更新内容2 ----------------------------- //
            let time = setInterval(() => { // 等待地图挂载后再自定义标点
				if (mapObj != null) {
					this.setIcon(); // 挂载坐标图片信息
					this.getCoordinateList(type); // 获取坐标
					clearInterval(time);
				}
			}, 100)
          	// ---------------------------------------------------------------------- //
		},
         methods: { 
            loadScrpit() { // 挂载动态js
				var script = document.createElement('script');
				script.src = 'https://webapi.amap.com/maps?v=1.4.15&key=(这里是你申请的KEY)&callback=mapInit';
				document.body.appendChild(script);
			},
     		// ------------------------------ 更新内容3 ----------------------------- //
             setIcon() { // 设置icon
				// 创建 AMap.Icon 实例
				icon = new AMap.Icon({
					size: new AMap.Size(48, 60), // 图标尺寸
					image: "image.png", // Icon的图像
					imageSize: new AMap.Size(48, 60), // 根据所设置的大小拉伸或压缩图片
					imageOffset: new AMap.Pixel(0, 0) // 图像相对展示区域的偏移量,适于雪碧图等
				});
			},
			getCoordinateList(type) { // 获取坐标
				uni.request({
					url: this.url_str, // 你的请求后台的地址
					method: 'GET',
					data: {
						region: '你的地区参数',
					},
					success: (e) => {
						console.log(e);
						mapObj.remove(punctuation);
						this.setMapMarkerList(this.list); // 赋予坐标图片标点
					}
				})
			},
            setMapMarkerList(list) { // 批量生成标点
				let that = this;
				// console.log(list);
				list.forEach(function(marker) {
					let punctuationItem = new AMap.Marker({
						map: mapObj,
						icon: icon,
						position: [marker.Longitude, marker.Latitude],
						offset: new AMap.Pixel(-13, -30)
					});
					punctuation.push(punctuationItem);
				});
			},
            // ---------------------------------------------------------------------- //
         }
    }
</script>
代码讲解

更新内容1:创建全局的图片的实例 icon,只需要创建一次,直到更换标点图案。全局的标点按需加载,每次跳转与切换坐标定位,都需要重新挂载。

let icon = null; // 生成标点图片实例
let punctuation = []; // 生成标点实例

更新内容2:在 onLoad() 里设置定时器监听地图是否挂载成功,没挂载地图实例前无法挂载标点图片与坐标标点。一旦监听到地图组件挂载便取消对应的定时器监听。

let time = setInterval(() => { // 等待地图挂载后再自定义标点
	if (mapObj != null) {
		this.setIcon(); // 挂载坐标图片信息
		this.getCoordinateList(type); // 获取坐标并挂载坐标标点
		clearInterval(time);
	}
}, 100)

更新内容3:

  • setIcon(): 设置自定义标点图片的参数,具体的参数看下面介绍,按照对应的测试一下就可以调整为自己想要的效果了。
  • getCoordinateList(): 请求获取所需的坐标信息,在赋予标点时需要根据对应的坐标信息进行挂载。这里需要注意的是 mapObj.remove(punctuation) 这行代码,它用于去除过往的标点,当你需要根据不同的地区筛选时就需要使用这个去除旧的标点。
  • setMapMarkerList():请求标点后,调用该函数传入标点坐标,通过循环批量生成标点,并且存入 punctuation 以备后期需要消除标点使用。
setIcon() { // 设置icon
	// 创建 AMap.Icon 实例
	icon = new AMap.Icon({
		size: new AMap.Size(48, 60), // 图标尺寸
		image: "image.png", // Icon的图像
		imageSize: new AMap.Size(48, 60), // 根据所设置的大小拉伸或压缩图片
		imageOffset: new AMap.Pixel(0, 0) // 图像相对展示区域的偏移量,适于雪碧图等
	});
},
getCoordinateList(type) { // 获取坐标
	uni.request({
		url: this.url_str, // 你的请求后台的地址
		method: 'GET',
		data: {
			region: '你的地区参数',
		},
		success: (e) => {
			console.log(e);
			mapObj.remove(punctuation);
             this.list = e.data.list;
			this.setMapMarkerList(this.list); // 赋予坐标图片标点
		}
	})
},
setMapMarkerList(list) { // 批量生成标点
	let that = this;
	// console.log(list);
	list.forEach(function(marker) {
		let punctuationItem = new AMap.Marker({
			map: mapObj,
			icon: icon,
			position: [marker.Longitude, marker.Latitude],
			offset: new AMap.Pixel(-13, -30)
		});
		punctuation.push(punctuationItem);
	});
},

5、自定义信息框体

实现信息框体功能,实际上就是为标点绑定一个点击事件,点击后再对相应的标点进行渲染。难点主要在如何渲染信息窗体数据并且在信息窗体内绑定点击事件。

效果

image-20230616154841089

代码
<template>
	<!-- 地图底框 -->
	<view id="wrapper"></view>
</template>
<script>
    let mapObj = null; // 生成地图实例
    let icon = null; // 生成图片实例
	let punctuation = []; // 生成标点实例
    // ------------------------------ 更新内容1 ----------------------------- //
    let infoWindow = null // 信息窗体
	let isShowText = false; // 窗体是否打开
    // ---------------------------------------------------------------------- //
    window.mapInit = function() {
		mapObj = new AMap.Map('wrapper', {
			center: [Longitude, Latitude],
			layers: [
				new AMap.TileLayer.Satellite(), // 卫星
				new AMap.TileLayer.RoadNet() // 路网
			],
			zoom: 13
		});
        // ------------------------------ 更新内容2 ----------------------------- //
        mapObj.on('click', () => {
			if (isShowText) {
				isShowText = false;
				infoWindow.close();
			}
		});
		infoWindow = new AMap.InfoWindow({
			offset: new AMap.Pixel(0, -30),
			isCustom: true,
		});
        // ---------------------------------------------------------------------- //
		// 在这里可以做一些回调处理  定位 标记点 等等  或者把amap弄成一个全局变量
		// 引入单个插件
		AMap.plugin('AMap.ToolBar', function() { // 异步加载插件
			var toolbar = new AMap.ToolBar();
			map.addControl(toolbar);
		});
	};
    export default {
        data() {}onLoad() {
			// #ifdef H5
			this.loadScrpit();
			// #endif
            let time = setInterval(() => { // 等待地图挂载后再自定义标点
				if (mapObj != null) {
					this.setIcon(); // 挂载坐标图片信息
					this.getCoordinateList(type); // 获取坐标
					clearInterval(time);
				}
			}, 100)
		},
         // ------------------------------ 更新内容3 ----------------------------- //
         created() {
			let that = this;
			window.getParkDetail = function() {
				that.isBottomShow = false;
				that.isList = false;
				isShowText = false;
				infoWindow.close();
			};
		},
        // ---------------------------------------------------------------------- //
         methods: { 
            loadScrpit() { // 挂载动态js
				var script = document.createElement('script');
				script.src = 'https://webapi.amap.com/maps?v=1.4.15&key=(这里是你申请的KEY)&callback=mapInit';
				document.body.appendChild(script);
			},
             setIcon() { // 设置icon
				...
			},
			getCoordinateList(type) { // 获取坐标
				...
			},            
            setMapMarkerList(list) { // 批量生成标点
				let that = this;
				// console.log(list);
				list.forEach(function(marker) {
					let punctuationItem = new AMap.Marker({
						map: mapObj,
						icon: icon,
						position: [marker.Longitude, marker.Latitude],
						offset: new AMap.Pixel(-13, -30)
					});
                      // ----------------------------- 更新内容4 ----------------------------- //
                      punctuationItem.Id = marker.Id;
					punctuationItem.on('click', that.markerClick);
                      // -------------------------------------------------------------------- //
					punctuation.push(punctuationItem);
				});
			},
             // ------------------------------ 更新内容5 ----------------------------- //
             markerClick(e) {
				let that = this;
				uni.request({
					url: this.url_str,
					method: 'GET',
					data: {
						Id: e.target.Id,
					},
					success: (res) => {
						let content =
							`<div style="width: 18.1vw;height: 37vh;background: rgba(15, 54, 94, .8);display: flex;justify-content: center;align-items: center;">
							<div style="width: 17.7vw;height: 36.3vh;border: 0.1vw solid rgba(211, 243, 255, .3);display: flex;justify-content: flex-start;align-items: center;flex-flow: column;position: relative;">
								<div style="background: #071C33;width: 16.5vw;height: 14.8vh;margin-top:1.5vh;">
									<image style="background: #071C33;width: 100%;height: 100%;" src="${res.Img}"></image>
								</div>
								<div style="width: 16.5vw;font-size: 0.9vw;font-family: PingFang SC-Medium, PingFang SC;font-weight: 500;color: #00BAFF;margin-top:1.9vh;">${res.Name}</div>
								<div style="width: 16.5vw;display: flex;justify-content: flex-start;align-items: flex-start;font-size: 0.8vw;font-family: PingFang SC-Regular, PingFang SC;font-weight: 400;color: #D3F3FF;margin-top:0.6vh;">
									<div style="width: 4.2vw;">项目区域:</div>
									<div style="width: 10.5vw;">${res.Region}</div>
								</div>
								<div style="width: 16.5vw;display: flex;justify-content: flex-start;align-items: flex-start;font-size: 0.8vw;font-family: PingFang SC-Regular, PingFang SC;font-weight: 400;color: #D3F3FF;margin-top:0.6vh;">
									<div style="width: 4.2vw;">项目地址:</div>
									<div style="width: 10.5vw;">${res.Region}${res.Address}</div>
								</div>
								<div style="width: 16.5vw;display: flex;justify-content:flex-start;align-items: flex-start;font-size: 0.8vw;font-family: PingFang SC-Regular, PingFang SC;font-weight: 400;color: #D3F3FF;margin-top:0.6vh;">
									<div>入驻企业:</div>
									<div>${res.Client}</div>
								</div>
								<div style="cursor: pointer;width: 16.5vw;text-align: right;font-size: 0.8vw;font-family: PingFang SC-Regular, PingFang SC;font-weight: 400;color: #00BAFF;" οnclick="(function (){this.window.getParkDetail();})()">查看详情</div>
								<div style="width: 1.2vw;height: 1.2vw;position: absolute;bottom: 0vh;left: 0;">
									<image style="width: 100%;height: 100%;" src="https://iot-h-shock-1256963066.cos.ap-guangzhou.myqcloud.com/http%3A//tmp/aUK2XDny3F6c39312fd315dc7cef413386aa08e70345.png"></image>
								</div>
							</div>
						</div>`;
						infoWindow.setContent(content);
						infoWindow.open(mapObj, e.target.getPosition());
						isShowText = true;
					}
				})
			},
             // ---------------------------------------------------------------------- //
         }
    }
</script>
代码讲解

更新内容1:创建全局的的实例 infoWindow ,也是只要挂载一次即可。主要用于信息窗体的打开与关闭。

isShowText 是用于判断当前的窗体是否打开,在点击或移动地图时关闭窗体。

let infoWindow = null // 信息窗体
let isShowText = false; // 窗体是否打开

更新内容2:mapObj 是地图实例,可以给它绑定点击事件,根据 isShowText 的状态来判定是否开启了窗体。infoWindow.close() 方法用于关闭当前窗体。

窗体的方法调用不用等待 new AMap.InfoWindow() 创建实例,因为没有也触发不了关闭🤣。

  • offset:信息窗体距离标点的偏移量,这个具体的参数需要你们自己调整了。
  • isCustom:是指是否开启自定义信息窗体,这个设置为 true 就好了。
mapObj.on('click', () => {
	if (isShowText) {
		isShowText = false;
		infoWindow.close();
	}
});
infoWindow = new AMap.InfoWindow({
	offset: new AMap.Pixel(0, -30),
	isCustom: true,
});

更新内容3:这个方法是直接挂载在window对象上,这样才可以被信息窗体内的事件所触发。主要用于关闭当前的信息窗体,打开某个界面或是跳转到某个链接网址。

需要的操作就写在这里面 getParkDetail() 这个函数名不是固定的,你可以根据自己的业务功能重新命名。

created() {
	let that = this;
	window.getParkDetail = function() {
		that.isBottomShow = true; // 打开某个显示框
		that.isList = true; // 打开某个列表
		isShowText = false;
		infoWindow.close();
	};
},

更新内容4:这个 Id 就只是当初的用做全球的参数,如果你不需要的话也不用加上。重点在于绑定的点击事件。这个点击事件决定了信息窗体的挂载,当点击标点时,触发该动态渲染信息窗体的方法。

punctuationItem.Id = marker.Id;
punctuationItem.on('click', that.markerClick);

更新内容5:我们信息窗体里的内容肯定是动态渲染的,这就需要我们去重新请求来获取具体的详情。

infoWindow.setContent() 方法可以向当前的信息窗体实例添加自定义的内容,因为太长了,我移除了大段的代码,具体的写法请参照上文,在下文 content 的内容里会做一部分的讲解。

我们通过 infoWindow.open() 方法打开当前的信息窗体,第一个参数是当前挂载信息窗体的地图 mapObj ,第二个参数就是它当前的位置的获取方法,这个是固定的没有什么需要注意。

markerClick(e) {
	let that = this;
	uni.request({
		url: this.url_str,
		method: 'GET',
		data: {
			Id: e.target.Id,
		},
		success: (res) => {
			let content; // 添加的内容,详见下文
			infoWindow.setContent(content); // 渲染
			infoWindow.open(mapObj, e.target.getPosition());
			isShowText = true;
		}
	})
},

content 的内容:

  • 这里因为我们需要动态添加内容,我们需要使用到 模板字符串 的格式,直接无提示写 dom 确实比较麻烦,新手记不住可以先去把样式写好再拷进来。这里是必须写原生,不然无法正常显示。

  • 在宽高这里,它是支持 vw 和 vh 的,写大屏的时候可以用这个,或者你直接写固定也可以。

  • 绑定点击事件这里必须用自启动函数来调用你挂载在 window 对象的方法,不然没有办法直接对事件进行处理,如果你有更好的方法欢迎评论区补充指正👍。

  • ${res.Client} 就是模板字符串的写法,没有很难的地方,详见 模板字符串 的文档。

let content = 
`<div style="width: 18.1vw;height: 37vh;">
		<div οnclick="(function (){this.window.getParkDetail();})()">查看详情</div>
		<div>
			<image style="width: 100%;height: 100%;" src="${res.Client}"></image>
		</div>
</div>`

四、结尾

本文仅记录我个人的解决思路,在封装或是调用等方面上会有所欠缺,欢迎大佬指正补充相关方面的技术博客与文档。如果有什么疑问也欢迎大家评论区或是私信我,我看见就会回复。

如果本文有帮助到你,可否给我一个赞,你的支持就是我创作的动力👍。

  • 34
    点赞
  • 124
    收藏
    觉得还不错? 一键收藏
  • 51
    评论
在React中使用高德地图自定义信息窗体,可以通过以下步骤实现: 1. 安装必要的依赖:您需要安装`react-amap`库,该库提供了React组件来与高德地图进行交互。您可以使用npm或yarn来安装它。 ``` npm install react-amap ``` 2. 导入所需组件和样式:在您的React组件文件中导入所需的组件和样式。 ```jsx import { Map, InfoWindow } from 'react-amap'; import 'react-amap/lib/style/index.css'; ``` 3. 创建地图组件:在您的React组件中创建地图组件,并设置必要的属性。 ```jsx const MapComponent = () => { const mapEvents = { created: (mapInstance) => { // 在地图创建完成后,进行相应的操作 }, }; return ( <Map events={mapEvents} zoom={10} center={{ longitude: 120, latitude: 30 }} > {/* 在这里添加其他地图相关的组件和标记 */} </Map> ); }; ``` 4. 创建自定义信息窗体组件:在地图组件中创建自定义信息窗体组件,并设置相应的内容。 ```jsx const CustomInfoWindow = () => { const infoWindowEvents = { close: () => { // 在信息窗体关闭时,进行相应的操作 }, }; return ( <InfoWindow position={{ longitude: 120, latitude: 30 }} isCustom={true} content={<div>这是自定义信息窗体内容</div>} events={infoWindowEvents} /> ); }; ``` 5. 将自定义信息窗体组件添加到地图中:在地图组件中将自定义信息窗体组件添加到地图中。 ```jsx const MapComponent = () => { // ... return ( <Map events={mapEvents} zoom={10} center={{ longitude: 120, latitude: 30 }} > <CustomInfoWindow /> </Map> ); }; ``` 通过以上步骤,您就可以在React中使用高德地图自定义信息窗体了。您可以根据需要调整自定义信息窗体的样式和内容。请注意,您需要替换示例中的经纬度和内容为您实际需要显示的信息

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 51
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值