前言:
根据百度地图api示例,调用地图需要先引入js:
<script type="text/javascript" src="//api.map.baidu.com/api?v=2.0&ak=您的密钥"></script>
之后才能进行各种api调用,但angular直接调用互联网的js,需要利用angular-cli.json的script属性或者直接在index.html中引入js,这种全局调用的方式并不是笔者想要的。
于是就去搜索了各种angular异步调用js方法,在StackOverFlow一篇问答:angular如何动态加载外部js 里有介绍说用system.js或者scriptjs工具来加载,笔者尝试了scriptjs加载的方法,发现运行时提示警告:
depends on 'scriptjs'. CommonJS or AMD dependencies can cause optimization bailouts.
StackOverFlow上类似问题的解释是:When you use a dependency that is packaged with CommonJS, it can result in larger slower applications
看来这种方式也不是很好,会导致打包后的程序更慢更大,于是笔者就想到直接找别人写好的轮子岂不是更快,最后在GitHub上找到angular-baidu-maps,看到下面调用代码大喜:
import { AbmModule } from 'angular-baidu-maps';
@NgModule({
imports: [
BrowserModule,
AbmModule.forRoot({
apiKey: '' // app key为必选项
})
],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule { }
看起来很好用,这就开整!
安装&升级Angular12踩坑
根据上面GitHub的README.md可以很快安装成功,但当笔者调用时候却报了提示angular版本不兼容,查了下发现,插件是angular12版本,而项目用的却是angular11,咨询了项目负责人后获得了升级许可,升级完终于可以调用成功了,但却发现之前PrimeNG的css样式都失效了,去官网看了下才发现原来,PrimeNG 12之后css改为primeflex3.0,于是又是一番折腾,终于把样式改正常了,虽然运行终端log里一直有大量的下面这种警告:
assets/css/style-blessed.scss - Warning: Css Minimizer Plugin: > assets/css/style-blessed.scss:2700:1: warning: Expected identifier but
found "."
2700 │ .social-icon {
╵ ^
查了下应该是css压缩转换相关的问题,但是试了各种方法依然没能解决,虽然不影响运行和打包,但还是挺烦人的,如果有大佬有好的解决办法,希望可以留言告诉笔者,万分感谢!
正式使用
需求:
- 添加大量自定义标签;
- 点击自定义标签显示信息窗;
- 获取用户定位并显示;
添加大量自定义标签
需要使用到BMapLib.MarkerClusterer类,这个类是另外加载的,所以我们要在app.module.ts引用时在AbmModule的libraries里添加上:
//baidu map
AbmModule.forRoot({
apiKey: '你的apiKey',
libraries: [
'//api.map.baidu.com/library/TextIconOverlay/1.2/src/TextIconOverlay_min.js',
'//api.map.baidu.com/library/MarkerClusterer/1.2/src/MarkerClusterer_min.js',
],
}),
接着在调用api的组件里添加定义:
declare const BMapLib: any;
之后如果你之前已经写好标签数组(markers)就可以像这样直接调用了:
new BMapLib.MarkerClusterer(map, {markers: markers})
我们这里先从创建自定义标签开始:
// 创建一个包含经纬度的点
const point = new BMap.Point(lng, lat);
// 创建一个自定义图标,包含图片路径和一个包含宽高的Size对象
const myIcon = new BMap.Icon(
'/img/map/marker_icon.png',
new BMap.Size(iconWidth, iconHeight)
);
// 创建标注
const marker = new BMap.Marker(point, { icon: myIcon });
// 设置标注标题
marker.setTitle(name);
于是一个自定义标注完成了,之后只要将标注用MarkerClusterer聚合:
// 写在循环内,将所有标注放到一个数组里
markers.push(marker)
// 标注导入聚合
const markerClusterer = new BMapLib.MarkerClusterer(map, {markers: markers})
// 聚合点样式(和标注类似,多了一个聚合后文字的颜色)
const polymerizationStyles = [
{
url: '/img/map/cluster_icon.png',
size: new BMap.Size(clusterIconWidth, clusterIconHeight),
textColor: #FFF,
},
];
// 设置超过几个点开始聚合
markerClusterer.setMinClusterSize(2);
// 调用样式
markerClusterer.setStyles(polymerizationStyles);
到这里,信息窗就完成了,下面开始获取用户定位
点击自定义标签显示信息窗
信息框添加本身比较简单,可以用js原始的添加删除事件方法,我这里为了统一管理事件用了 rxjs:
// 引入rxjs相关函数
import { Subscription, fromEvent, merge } from 'rxjs';
// 定义一个订阅器
subscription: Subscription;
下面代码是写在创建marker的循环里的:
// 信息框内容
const info_html = `<div><span>我是信息窗信息</span></div>`;
// 点击标注显示信息窗()
const markerEvent = fromEvent(marker, 'click').pipe(
tap((e) => marker.openInfoWindow(new BMap.InfoWindow(info_html)))
);
// 事件和marker一样用一个数组聚合
events.push(markerEvent);
之后在循环外面将事件聚合和发布:
// merge聚合全部点击事件, 再通一发布
this.subscription = merge(...events).subscribe();
这样我们就能在ngOnDestroy()里轻松销毁所有事件:
ngOnDestroy(): void {
// 销毁事件
this.subscription.unsubscribe();
}
到这里,大量自定义标签的添加就完成了,下面开始添加信息窗
获取用户定位并显示
获取用户定位我们需要用到BMap的Geolocation方法:
// 定位所需的成功状态
declare const BMAP_STATUS_SUCCESS: any;
// 获取当前位置经纬度
const geolocation = new BMap.Geolocation();
// 为了在下面匿名函数内调用到组件对象
const _this = this;
geolocation.getCurrentPosition(function (r) {
if (this.getStatus() == BMAP_STATUS_SUCCESS) {
console.log('您的位置:' + r.point.lng + ',' + r.point.lat);
//自定义定位标识(和marker一样)
const myCurIcon = new BMap.Icon(
'/img/map/cluster_icon.png',
new BMap.Size(curIconWidth, curIconHeight)
);
// 创建当前位置标注
const curMarker = new BMap.Marker(r.point, { icon: myCurIcon });
curMarker.setTitle(curIconTitle);
//将当前位置标注添加到地图
map.addOverlay(curMarker);
// 这里的curLat和curLng是需要输出到父组件的
// @Output() curLat = new EventEmitter<string>();
_this.curLat.emit(r.point.lat);
_this.curLng.emit(r.point.lng);
} else {
console.log('failed' + this.getStatus());
alert('获取当前位置失败,请开启定位权限!');
}
});
这样就能拿到用户的定位并显示了,父组件拿到定位后也可以进行调用百度计算路径的网址:
/**
* 参数说明:
* mode:出行方式,这里用的是开车 - driving
* region:地域,这里的是西安(似乎随便填一个就行
* origin:起始地经纬度,这里传入了子组件传来的经纬度
* destination:终点经纬度
*/
<a
[href]="'http://api.map.baidu.com/direction?mode=driving&output=html®ion=%E8%A5%BF%E5%AE%89%E2%80%8B&origin='
+ curLat + ',' + curLng
+ '&destination='
+ lat + ',' + lng"
target="_blank"
>
2021/8/27 更新 获取当前位置经纬度方法2
由于需求增加,需要在加载百度页面的其他页面也要获取到当前位置经纬度,于是搜寻了下js原生的获取方法:
ngOnInit() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition((p) => {
const { latitude, longitude } = p.coords;
console.log('latitude, longitude: ' , latitude, longitude);
});
}
}
这个方法简单,而且可以直接在初始化时候调用,不用等待百度地图相关api的启用。
参考资料&鸣谢: