@heycar/heycars-map
TypeScript icon, indicating that this package has built-in type declarations

2.5.3 • Public • Published

说明

时间关系,先写个简要文档,之后会补一个全面的文档。

特性

  • 集成 高德 和 谷歌 两个地图
  • 支持 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 供应商进行测试。

测试时的注意点:

  1. 起点和终点 在 splytech 后台系统地图上的位置是错误的。

为了方便测试 司机位置偏移 的优化效果,在 enableAuxiliaryGraspRoad 模式下会显示下列辅助信息:

  1. 会显示两个导航路线,蓝色跟设计稿样式一致的是原来的导航路径,红色的是司机位置纠偏以后计算的导航路径。
  2. 会显示两个车辆,跟设计稿样式一致的是原来的车辆,虚影的车辆是司机位置纠偏以后的车辆。
  3. 会将供应商提供的司机位置显示为蓝色标记,辅助观察供应商提供的司机位置。
  4. 会显示一条黑色的路径,表示将供应商提供的位置纠偏以后的路径。
  5. 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;

Readme

Keywords

none

Package Sidebar

Install

npm i @heycar/heycars-map

Weekly Downloads

10

Version

2.5.3

License

none

Unpacked Size

10 MB

Total Files

856

Last publish

Collaborators

  • kuangweihang
  • alan1034
  • situgl
  • lldf1984
  • xinchaobeta