@heycar/heycars-map v2.5.1
说明
时间关系,先写个简要文档,之后会补一个全面的文档。
特性
- 集成 高德 和 谷歌 两个地图
- 支持 vue 2.7 和 vue 3
- 统一的接口,不需要写两套
安装
# 安装依赖包
npm install @heycar/heycars-map --save
加载地图样式
import "@heycar/heycars-map/dist/style.css";
自定义字体
目前地图提供了 3 个字体 css variables 用于外部制定特殊字体
/* 使用特殊字体 HarmonyOS_Sans_SC_Regular */
body {
--HEYCAR_MAP_CSS_VAR_FONT_REGULAR: HarmonyOS_Sans_SC_Regular, "PingFangSC-Regular", "PingFang SC";
--HEYCAR_MAP_CSS_VAR_FONT_MEDIUM: HarmonyOS_Sans_SC_Medium, "PingFangSC-Medium", "PingFang SC";
--HEYCAR_MAP_CSS_VAR_FONT_SEMI_BOLD: HarmonyOS_Sans_SC_Bold, "PingFangSC-Semibold", "PingFang SC";
}
使用
常用数据结构
export type CoordinateType = "wgs84" | "gcj02" | "bd09";
export type CoordinatePoint = {
lng: number;
lat: number;
type: CoordinateType;
};
export type CoordinatePlace = {
lng: number;
lat: number;
type: CoordinateType;
name: string;
displayName: string;
};
export interface CoordinateZone {
name: string;
path: CoordinatePoint[];
}
export interface CoordinateRecommendZonePlaces {
// 是否强制吸附
adsorption?: boolean;
type?: RecommendType;
// 是否可以提供服务
available?: boolean;
zone?: CoordinateZone;
places?: CoordinatePlace[];
}
export interface CoordinateValueOfOnChangeRecommendPlace {
type: RecommendType;
isSameZone: boolean;
place: CoordinatePlace;
inputPlace: CoordinatePlace;
zone?: CoordinateZone;
}
export interface CoordinateGeoPosition {
position: CoordinatePoint;
coords?: GeolocationCoordinates;
}
export interface CoordinateTrackPoint {
lng: number;
lat: number;
type: CoordinateType;
angle?: number;
speed?: number;
timestamp: number;
}
export enum CenterPlaceStatus {
GEO_LOADING = "GEO_LOADING",
QUERYING_INFO = "QUERYING_INFO",
SERVICE_UNAVAILABLE = "SERVICE_UNAVAILABLE",
OK = "OK",
}
export type GoogleConnectionStatus = "pending" | "connected" | "unconnected";
// GeolocationPositionError 类型参考 [MDN Reference](https://developer.mozilla.org/docs/Web/API/GeolocationPositionError)
export interface BusinessGeolocationPositionError extends GeolocationPositionError {
// isBusinessTimeout 是产品定义的 业务超时,不同于标准 GeolocationPositionError 的TIMEOUT
// 参考 http://p.heycars.cn/zentaopms/www/index.php?m=story&f=view&storyID=1212
// 参考 https://doc.weixin.qq.com/doc/w3_AZIAowasAIcGieCNESnQZe1rhTyaK?scode=ACcAmge7AAou1TTqYiAZIAowasAIc
isBusinessTimeout: boolean;
}
// Restricted(绿区)Forbidden(红区)Recommend(推荐点)
export type RecommendType = "Restricted" | "Forbidden" | "Recommend";
在 入口文件添加 MapProvider
- 对于 jsx/tsx 文件的例子
<MapProvider
// 高德地图 api key
amapKey={amapApiKey}
// 高德地图 secret
amapSecret={amapApiSecret}
// 谷歌地图 id
gmapId={gmapId}
// 谷歌地图 api key
gmapKey={gmapApiKey}
// 使用哪个地图供应商,目前两个供应商: amap 高德 / gmap 谷歌
supplier={"amap"}
// 地图加载失败时显示的标题
renderLoadFailedTitle={() => (supplier === "gmap" ? "未能成功访问谷歌地图" : "高德地图加载失败")}
// 地图加载失败时显示的描述
renderLoadFailedDescription={() =>
supplier === "gmap" ? "请确认您的网络能正常访问谷歌地图, \n或切回高德地图" : undefined
}
>
...
</MapProvider>
- 对于 vue 文件的例子
<MapProvider
:gmap-id="gmapId"
:gmap-key="gmapApiKey"
:amap-key="amapApiKey"
:amap-secret="amapApiSecret"
supplier="amap"
render-load-failed-title='() => supplier === "gmap" ? "未能成功访问谷歌地图" : "高德地图加载失败"'
render-load-failed-description='() => supplier === "gmap" ? "请确认您的网络能正常访问谷歌地图,\n或切回高德地图" : undefined'
>
...
</MapProvider>
在地图里可以使用的组件
为了方便集成,已经将常用业务逻辑集成在四个业务组件里面,下面是推荐使用的业务组件
BusinessRecomendPlaceMap
BusinessReselectPlaceMap
BusinessQuotingMap
BusinessTaxiServiceMap
BusinessTaxiEndMap
下面三个是推荐搭配使用的业务 hooks
useBusinessRecomendPlaceMap
useBusinessReselectPlaceMap
useBusinessQuotingMap
useBusinessTaxiServiceMap
选择上车点和推荐点的地图组件 BusinessRecomendPlaceMap
对于 jsx/tsx 文件的例子
import { defineComponent } from "vue";
import { BusinessRecomendPlaceMap, useBusinessRecomendPlaceMap } from "@heycar/heycars-map";
export default defineComponent({
setup() {
const {
centerPlace,
mapContext,
setCenterPlaceByUserSpecified,
setCenterPlaceByUserSpecifiedInZone,
} = useBusinessRecomendPlaceMap();
// 演示 setCenterPlaceByUserSpecifiedInZone 的用法
const demoForUsage = () => {
setCenterPlaceByUserSpecifiedInZone({
// 期望的地图中心点
place: {
lng: 139.56,
lat: 35.56,
type: "gcj02",
name: "user specified place in zone",
displayName: "user specified place in zone",
},
// 推荐点和绿区信息
recommends: {
// 绿区
zone: {
name: "zone 1",
path: [
{ lng: 139.569, lat: 35.555, type: "gcj02" },
{ lng: 139.558, lat: 35.55, type: "gcj02" },
{ lng: 139.56, lat: 35.565, type: "gcj02" },
],
},
// 绿区内的推荐点列表
places: [
{
lng: 139.56,
lat: 35.56,
type: "gcj02",
name: "user specified place in zone",
displayName: "user specified place in zone",
},
{
lng: 139.562,
lat: 35.562,
type: "gcj02",
name: "recommend place 2",
displayName: "recommend place 2",
},
],
},
});
};
return () => (
<BusinessRecomendPlaceMap
class={"demo"}
geoLoadingTitle={"正在获取您当前的位置"}
unavailableTitle={"当前区域暂未开通服务"}
forbiddenTitle={"当前区域不可叫车"}
emptyTitle={"当前位置"}
queryingTitle={"正在获取地址信息"}
recomendDescription={"您将在此处上车"}
geoErrorOnceNotificationKey="BusinessReselectPlaceMap_GeoErrorOnceKey"
defaultCenterPlace={(place) =>
place ?? {
lng: 139.777777,
lat: 35.777777,
type: "gcj02",
name: "default place name",
displayName: "default place displayName",
}
}
getAvailable={() => Promise.resolve(true)}
getRecomendPlace={getRecomendPlace}
getDefaultCenterPlace={getDefaultCenterPlace}
renderPlacePhoto={(place) => {
place;
return "https://oss-now.heycars.cn/image/graphicGuidance/file/hmlh38_xs6_DdksNX0_TbgF0lKXp.jpg";
}}
renderPlaceTag={(place) => {
place;
return "最近使用";
}}
mapContext={mapContext}
onChangePlace={(place) => {
console.log("地图中心点变化时触发 place = ", place);
}}
onChangeRecomandPlace={({ place, inputPlace, type, zone }) => {
console.log("用户操作地图,计算推荐点后得出的最终位置时触发,此时可以向后端查询城市信息");
console.log(
"计算推荐点之前的地址是: ",
inputPlace,
" 最终的地址是: ",
place,
" 推荐类型: ",
type,
"绿区(或红区):",
zone,
);
}}
onClickLocatorText={() =>
console.log("用户点击了蓝色光标文字触发,此时可以执行用户点击起点输入框相同的逻辑")
}
onClickLocatorPhoto={() =>
console.log("用户点击了蓝色光标图片触发,此时可以执行用户点击起点输入框相同的逻辑")
}
onGeoError={({ isBusinessTimeout }) => {
console.log(
"获取GPS失败时触发,此时可以弹框告诉用户",
isBusinessTimeout ? "给用户超时提示" : "给用户无权限反馈",
);
}}
/>
);
},
});
选择上车点和推荐点的地图组件 BusinessReselectPlaceMap
对于 jsx/tsx 文件的例子
import { defineComponent } from "vue";
import { BusinessReselectPlaceMap, useBusinessReselectPlaceMap } from "@heycar/heycars-map";
export default defineComponent({
setup() {
const {
centerPlace,
mapContext,
setCenterPlaceByUserSpecified,
setCenterPlaceByUserSpecifiedInZone,
} = useBusinessReselectPlaceMap();
// 演示 setCenterPlaceByUserSpecifiedInZone 的用法
const demoForUsage = () => {
setCenterPlaceByUserSpecifiedInZone({
// 期望的地图中心点
place: {
lng: 139.56,
lat: 35.56,
type: "gcj02",
name: "user specified place in zone",
displayName: "user specified place in zone",
},
// 推荐点和绿区信息
recommends: {
// 绿区
zone: {
name: "zone 1",
path: [
{ lng: 139.569, lat: 35.555, type: "gcj02" },
{ lng: 139.558, lat: 35.55, type: "gcj02" },
{ lng: 139.56, lat: 35.565, type: "gcj02" },
],
},
// 绿区内的推荐点列表
places: [
{
lng: 139.56,
lat: 35.56,
type: "gcj02",
name: "user specified place in zone",
displayName: "user specified place in zone",
},
{
lng: 139.562,
lat: 35.562,
type: "gcj02",
name: "recommend place 2",
displayName: "recommend place 2",
},
],
},
});
};
return () => (
<BusinessReselectPlaceMap
class={"demo"}
unavailableTitle={"当前区域暂未开通服务"}
forbiddenTitle={"当前区域不可叫车"}
emptyTitle={"当前位置"}
queryingTitle={"正在获取地址信息"}
recomendDescription={"您将在此处上车"}
// 对于同一个 geoErrorOnceNotificationKey
// geoErrorOnce 事件在 geoErrorOnceNotificationInterval 时间间隔内全局只会触发一次
geoErrorOnceNotificationKey="BusinessReselectPlaceMap"
defaultPlace={{
lng: 139.777777,
lat: 35.777777,
type: "gcj02",
name: "default place name",
displayName: "default place display name",
}}
getAvailable={() => Promise.resolve(true)}
getRecomendPlace={async ({ lng, lat, type }) => {
// 向后端获取推荐点信息
return {
// 服务是否可用
available: true,
// 推荐类别
type: "Restricted",
// 绿区
zone: {
name: "绿区名称",
path: [
{ lng: lng - 0.001, lat: lat + 0.001, type },
{ lng: lng, lat: lat - 0.001, type },
{ lng: lng + 0.001, lat: lat + 0.0005, type },
],
},
// 推荐点列表
places: [
{
lat: lat - 0.00001,
lng: lng + 0.0001,
type,
name: "place 1",
displayName: "place 1",
},
{
lat: lat - 0.0002,
lng: lng + 0.0002,
type,
name: "place 2",
displayName: "place 2",
},
{
lat: lat - 0.0002,
lng: lng - 0.0001,
type,
name: "place 3",
displayName: "place 3",
},
],
};
}}
onChangeRecomandPlace={({ place, inputPlace, type, zone }) => {
console.log("用户操作地图,计算推荐点后得出的最终位置时触发,此时可以向后端查询城市信息");
console.log(
"计算推荐点之前的地址是: ",
inputPlace,
" 最终的地址是: ",
place,
" 推荐类型: ",
type,
"绿区(或红区):",
zone,
);
}}
onClickLocatorText={() =>
console.log("用户点击了蓝色光标文字触发,此时可以执行用户点击起点输入框相同的逻辑")
}
onClickLocatorPhoto={() =>
console.log("用户点击了蓝色光标图片触发,此时可以执行用户点击起点输入框相同的逻辑")
}
onGeoError={({ isBusinessTimeout }) => {
console.log(
"获取GPS失败时触发,此时可以弹框告诉用户",
isBusinessTimeout ? "给用户超时提示" : "给用户无权限反馈",
);
}}
/>
);
},
});
询价业务的地图组件 BusinessQuotingMap
对于 jsx/tsx 文件的例子
import { defineComponent } from "vue";
import { BusinessQuotingMap, useBusinessQuotingMap } from "@heycar/heycars-map";
export default defineComponent({
setup() {
const { mapContext, toAmapCoordinateType } = useBusinessQuotingMap();
return () => (
<div>
<BusinessQuotingMap
class={"demo"}
from={{
displayName: "The Malayan Council",
name: "10 Winstedt Rd, #01-17, Singapore 227977",
type: "wgs84",
lat: 1.311295,
lng: 103.841974,
}}
to={{
displayName: "CDG Engie Charging Station",
name: "21 Kent Ridge Rd, Singapore 119220",
type: "wgs84",
lat: 1.2966426,
lng: 103.7763939,
}}
fromDescription={"您将在此上车"}
renderDescription={({ distance, duration, tolls }) =>
`全程 *${distance / 1000}公里* 约行驶 *${duration}* 高速费用 *${tolls ?? 0}*元`
}
mapContext={mapContext}
onClickStartPoint={(place) => console.log("点击起点时触发 palce = ", place)}
onClickEndPoint={(place) => console.log("点击终点时触发 palce = ", place)}
onChangeGoogleConnection={(status) =>
// 根据目前的产品需求,连通性检测只会被检测一次。
console.log("谷歌连通性发生变化时触发,连通性状态是:", status)
}
/>
<div>
经度 103.841974, 纬度 1.311295 在高德地图上的坐标类型是:
{toAmapCoordinateType([103.841974, 1.311295])}
</div>
<div>
);
},
});
打车状态流转业务的地图组件 BusinessTaxiServiceMap
对于 jsx/tsx 文件的例子
import { defineComponent } from "vue";
import { BusinessTaxiServiceMap, useBusinessTaxiServiceMap } from "@heycar/heycars-map";
export default defineComponent({
setup() {
const { mapContext } = useBusinessTaxiServiceMap();
return () => (
<BusinessTaxiServiceMap
class={css.adjustedDemo}
from={{
displayName: "The Malayan Council",
name: "10 Winstedt Rd, #01-17, Singapore 227977",
type: "wgs84",
lat: 1.311295,
lng: 103.841974,
}}
to={{
displayName: "CDG Engie Charging Station",
name: "21 Kent Ridge Rd, Singapore 119220",
type: "wgs84",
lat: 1.2966426,
lng: 103.7763939,
}}
driverStatus={"driverArrived"}
bookDispatchingTitle="2月14日 11:00 用车"
dispatchingTitle="正在为您搜索附近司机"
driverArrivedTitle="司机已等待 00:35"
renderStartSerivceTitle={({ distance, duration }) =>
`距你*${distance}*公里, *${duration}*分钟`
}
renderInServiceTitle={({ distance, duration }) =>
`距离终点*${distance}*公里, 预计*${duration}*分钟`
}
getDriverPositionTrack={async () => {
// 向后端请求司机的历史轨迹
return Promise.resolve<TrackPoint[]>([
{ lng: 121.4036983, lat: 31.216324, type: "gcj02", angle: 30, timestamp: 1698058438000 },
{ lng: 121.4036983, lat: 31.216324, type: "gcj02", angle: 30, timestamp: 1698058439000 },
{ lng: 121.403581, lat: 31.216415, type: "gcj02", angle: 30, timestamp: 1698058442000 },
]);
}}
interval={5000}
mapContext={mapContext}
/>
);
},
});
服务结束的地图组件
对于 jsx/tsx 文件的例子
import { defineComponent } from "vue";
import { BusinessTaxiEndMap } from "@heycar/heycars-map";
export default defineComponent({
setup() {
return () => (
<BusinessTaxiEndMap
class={"demo"}
from={{
displayName: "樟宜机场",
name: "樟宜机场",
lat: 1.35019,
lng: 103.994003,
type: "wgs84",
}}
to={{
lat: 1.346493,
lng: 103.746209,
name: "长城美华 Coffee Shop (CCMW)",
displayName: "长城美华 Coffee Shop (CCMW)",
type: "wgs84",
}}
/>
);
},
});
下面是基础业务组件的使用方法,但是目前阶段不推荐使用
下列是一些更加基础的业务组件, 虽然导出了,但是目前阶段不推荐使用
AbsoluteAddressBox
DrivingLine
PassengerCircle
PlaceCircle
StartEndPoint
TaxiCar
WalkingLine
WaveCircle
DrivingRoute
WalkingRoute
PickupPoints
在地图里使用 AddressBox
,需要放在 HeycarMap
内部
<HeycarMap center={[0, 0]} zoom={3}>
...
<AddressBox position={[0, 0]} title={"Martyrs Lawn"} description={"您将在此处上车"} />
...
</HeycarMap>
- 对于 vue 文件的例子
<HeycarMap :center="[0, 0]" class="any class name" :zoom="3">
<template #fallback>error</template>
<template #loading>loading</template>
...
<AddressBox title="Martyrs Lawn" :position="[0, 0]" description="您将在此处上车" >
</AddressBox>
...
</HeycarMap>
HeycarMap
的基本用法
- 对于 jsx/tsx 文件的例子
<HeycarMap
class="any class name"
// 地图加载失败要显示的内容
fallback={() => <div>error</div>}
// 地图还没有加载完成时要显示的内容
loading={() => <div>loading</div>}
// 地图中心点
center={[0, 0]}
// 地图放缩
zoom={3}
>
...
</HeycarMap>
- 对于 vue 文件的例子
<HeycarMap :center="[0, 0]" class="any class name" :zoom="3">
<template #fallback>error</template>
<template #loading>loading</template>
...
</HeycarMap>
关于 enableAuxiliaryGraspRoad 模式
enableAuxiliaryGraspRoad 模式适用场景:
- 测试司机位置偏移 的优化效果
- 目前只能在国内打车,测试优化效果。
- 目前只能用 splytech 供应商进行测试。
测试时的注意点:
- 起点和终点 在 splytech 后台系统地图上的位置是错误的。
为了方便测试 司机位置偏移 的优化效果,在 enableAuxiliaryGraspRoad 模式下会显示下列辅助信息:
- 会显示两个导航路线,蓝色跟设计稿样式一致的是原来的导航路径,红色的是司机位置纠偏以后计算的导航路径。
- 会显示两个车辆,跟设计稿样式一致的是原来的车辆,虚影的车辆是司机位置纠偏以后的车辆。
- 会将供应商提供的司机位置显示为蓝色标记,辅助观察供应商提供的司机位置。
- 会显示一条黑色的路径,表示将供应商提供的位置纠偏以后的路径。
- enableAuxiliaryGraspRoad 模式下,为了方便测试,取消了屏幕 15 秒自动调整功能。
下面是地图导出的一些基本工具
import { isCoordinatePointEqual } from "@heycar/heycars-map";
// 比较两个带有坐标类型的点是否为同一个点
isCoordinatePointEqual(
{ type: "gcj02", lng: 114.215114, lat: 22.215972 },
{ type: "wgs84", lng: 114.21016180538568, lat: 22.219570659901557 },
) === true;
9 months ago
9 months ago
11 months ago
11 months ago
11 months ago
11 months ago
10 months ago
10 months ago
11 months ago
10 months ago
11 months ago
11 months ago
11 months ago
1 year ago
12 months ago
12 months ago
12 months ago
12 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago