重构了geo相关的定义

This commit is contained in:
Xu Chang 2024-11-24 13:24:28 +08:00
parent 91cdd92881
commit 1447af1d37
26 changed files with 445 additions and 241 deletions

19
es/AspectDict.d.ts vendored
View File

@ -2,6 +2,8 @@ import { EntityDict, OperateOption, SelectOption, OperationResult, AggregationRe
import { AmapInstance } from "oak-external-sdk/lib/service/amap/Amap";
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
import { AsyncContext } from "oak-domain/lib/store/AsyncRowStore";
import { MapService } from "./types/Map";
import { GeoApi } from "./geo";
export declare type AspectDict<ED extends EntityDict & BaseEntityDict> = {
operate: <T extends keyof ED, OP extends OperateOption>(params: {
entity: T;
@ -51,19 +53,10 @@ export declare type AspectDict<ED extends EntityDict & BaseEntityDict> = {
getImportationTemplate: (params: {
id: string;
}, context: AsyncContext<ED>) => Promise<ArrayBuffer>;
searchPoi: (options: {
value: string;
areaCode?: string;
indexFrom?: number;
count?: number;
typeCode?: string;
}) => Promise<{
id: string;
areaId: string;
poiName: string;
detail: string;
coordinate: [number, number];
}[]>;
geoService: <ED extends BaseEntityDict & EntityDict, A extends GeoApi>(options: {
api: A;
params: Parameters<MapService[A]>[0];
}, context: AsyncContext<ED>) => Promise<ReturnType<MapService[A]>>;
loadRelations: (params: {
entities: (keyof ED)[];
}, context: AsyncContext<ED>) => Promise<ED['userRelation']['OpSchema'][]>;

26
es/geo.d.ts vendored
View File

@ -1,12 +1,18 @@
export declare function searchPoi(options: {
value: string;
areaCode?: string;
indexFrom?: number;
count?: number;
typeCode?: string;
}): Promise<{
id: string;
areaId: string;
import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore';
import { EntityDict } from 'oak-domain/lib/types';
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
import { MapService } from './types/Map';
export declare type GeoApi = 'geo' | 'regeo';
export declare function registerGetMapService(fn: <ED extends BaseEntityDict & EntityDict>(context: AsyncContext<ED>) => Promise<MapService>): void;
export declare function geoService<ED extends BaseEntityDict & EntityDict, A extends GeoApi>(options: {
api: A;
params: Parameters<MapService[A]>[0];
}, context: AsyncContext<ED>): Promise<{
poiName: string;
coordinate: [number, number];
areaId: string;
} | {
poiName: string;
areaId: string;
latitude: number;
longitude: number;
}[]>;

View File

@ -1,38 +1,11 @@
export async function searchPoi(options) {
const { value, areaCode, indexFrom, count } = options;
const form = new FormData();
form.set('stName', value);
if (areaCode) {
form.set('code', areaCode);
}
if (indexFrom && count) {
form.set('page', `${indexFrom / count}`);
form.set('size', `${count}`);
}
const result = await fetch('https://dmfw.mca.gov.cn/9095/stname/listPub', {
method: 'post',
body: form,
});
const { records } = await result.json();
const pois = await Promise.all(records.map(async (ele) => {
let { area, standard_name, gdm, id, province_name, city_name, area_name, place_type, } = ele;
// 对返回的area数据进行一些清洗不规范
if (area.length === 9) {
if (area.endsWith('999')) {
area = area.slice(0, 6);
}
}
if (area === '000000') {
// 搜索如长江这样的地理名称时会返回这样的数据,过滤掉
return undefined;
}
return {
id,
areaId: area,
poiName: standard_name,
coordinate: gdm.coordinates[0],
detail: `${province_name}${city_name}${area_name}${standard_name}(${place_type})`,
};
}));
return pois.filter((poi) => !!poi);
import { assert } from 'oak-domain/lib/utils/assert';
let GetMapServiceFn = undefined;
export function registerGetMapService(fn) {
GetMapServiceFn = fn;
}
export async function geoService(options, context) {
assert(GetMapServiceFn, '未注入指定的GetMapServiceFn');
const service = await GetMapServiceFn(context);
const { api, params } = options;
return await service[api](params);
}

6
es/index.d.ts vendored
View File

@ -2,7 +2,7 @@ import { operate, select, fetchRows, count, aggregate } from './crud';
import { amap } from './amap';
import { getTranslations } from './locales';
import { registerPorts, clearPorts, importEntity, exportEntity, getImportationTemplate } from './port';
import { searchPoi } from './geo';
import { geoService, registerGetMapService } from './geo';
import { loadRelations } from './relation';
import { crossBridge } from './utils';
declare const aspectDict: {
@ -16,10 +16,10 @@ declare const aspectDict: {
importEntity: typeof importEntity;
exportEntity: typeof exportEntity;
getImportationTemplate: typeof getImportationTemplate;
searchPoi: typeof searchPoi;
geoService: typeof geoService;
loadRelations: typeof loadRelations;
crossBridge: typeof crossBridge;
};
export default aspectDict;
export * from './AspectDict';
export { registerPorts, clearPorts, };
export { registerPorts, clearPorts, registerGetMapService, };

View File

@ -2,7 +2,7 @@ import { operate, select, fetchRows, count, aggregate } from './crud';
import { amap } from './amap';
import { getTranslations } from './locales';
import { registerPorts, clearPorts, importEntity, exportEntity, getImportationTemplate } from './port';
import { searchPoi } from './geo';
import { geoService, registerGetMapService } from './geo';
import { loadRelations } from './relation';
import { crossBridge } from './utils';
const aspectDict = {
@ -16,10 +16,10 @@ const aspectDict = {
importEntity,
exportEntity,
getImportationTemplate,
searchPoi,
geoService,
loadRelations,
crossBridge,
};
export default aspectDict;
export * from './AspectDict';
export { registerPorts, clearPorts, };
export { registerPorts, clearPorts, registerGetMapService, };

26
es/map/amap.d.ts vendored Normal file
View File

@ -0,0 +1,26 @@
import { MapService } from "../types/Map";
import { AmapKeyType } from 'oak-external-sdk/lib/types/AMap';
export default class AMap implements MapService {
keys: Array<{
key: string;
type: AmapKeyType;
count: number;
}>;
index: number;
constructor(keys: Array<{
key: string;
type: AmapKeyType;
}>);
protected getKey(): string;
regeo(param: {
latitude: number;
longitude: number;
}): Promise<{
poiName: any;
areaId: any;
}>;
geo(param: {
name: string;
areaId?: string;
}): Promise<any>;
}

57
es/map/amap.js Normal file
View File

@ -0,0 +1,57 @@
import assert from 'assert';
import AmapSDK from 'oak-external-sdk/lib/AmapSDK';
import { WebServiceKeyWeight } from 'oak-external-sdk/lib/types/AMap';
export default class AMap {
keys;
index;
constructor(keys) {
assert(keys.length > 0);
this.keys = keys.map(ele => ({
...ele,
count: WebServiceKeyWeight[ele.type],
}));
this.index = 0;
}
getKey() {
while (true) {
const key = this.keys[this.index];
if (key.count > 0) {
key.count--;
return key.key;
}
key.count = WebServiceKeyWeight[key.type];
this.index = (this.index + 1) % this.keys.length;
}
}
async regeo(param) {
const key = this.getKey();
const instance = AmapSDK.getInstance(key);
/**
* https://lbs.amap.com/api/webservice/guide/api/georegeo
*/
const { regeocode } = await instance.regeo(param);
return {
poiName: regeocode.formatted_address,
areaId: regeocode.addressComponent.towncode,
};
}
;
async geo(param) {
const key = this.getKey();
const instance = AmapSDK.getInstance(key);
/**
* https://lbs.amap.com/api/webservice/guide/api/georegeo
*/
const { name, areaId } = param;
const { geocodes } = await instance.geocode({
address: name,
city: areaId,
});
return geocodes.map((ele) => ({
poiName: ele.formatted_address,
areaId: ele.adcode,
latitude: ele.location[0],
longitude: ele.location[1],
}));
}
}

9
es/port.d.ts vendored
View File

@ -1,21 +1,22 @@
import { EntityDict } from 'oak-domain/lib/types/Entity';
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
import { Importation, Exportation } from 'oak-domain/lib/types/Port';
import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore';
export declare function registerPorts<ED extends EntityDict>(importations: Importation<ED, keyof ED, any, any>[], exportations: Exportation<ED, keyof ED, any, any>[]): void;
export declare function registerPorts<ED extends EntityDict & BaseEntityDict>(importations: Importation<ED, keyof ED, any, any>[], exportations: Exportation<ED, keyof ED, any, any>[]): void;
export declare function clearPorts(): void;
export declare function importEntity<ED extends EntityDict, Cxt extends AsyncContext<ED>>(params: {
export declare function importEntity<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>>(params: {
entity: string;
id: string;
file: any;
option: string;
s2jOpts?: string;
}, context: Cxt): Promise<ArrayBuffer | void>;
export declare function exportEntity<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED>>(params: {
export declare function exportEntity<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED>>(params: {
entity: T;
id: string;
filter?: ED[T]['Selection']['filter'];
properties?: Record<string, any>;
}, context: Cxt): Promise<ArrayBuffer>;
export declare function getImportationTemplate<ED extends EntityDict, Cxt extends AsyncContext<ED>>(params: {
export declare function getImportationTemplate<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>>(params: {
id: string;
}, context: Cxt): Promise<ArrayBuffer>;

18
es/types/Map.d.ts vendored Normal file
View File

@ -0,0 +1,18 @@
export interface MapService {
regeo: (param: {
latitude: number;
longitude: number;
}) => Promise<{
poiName: string;
areaId: string;
}>;
geo: (param: {
name: string;
areaId?: string;
}) => Promise<Array<{
poiName: string;
areaId: string;
latitude: number;
longitude: number;
}>>;
}

2
es/types/Map.js Normal file
View File

@ -0,0 +1,2 @@
;
export {};

19
lib/AspectDict.d.ts vendored
View File

@ -2,6 +2,8 @@ import { EntityDict, OperateOption, SelectOption, OperationResult, AggregationRe
import { AmapInstance } from "oak-external-sdk/lib/service/amap/Amap";
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
import { AsyncContext } from "oak-domain/lib/store/AsyncRowStore";
import { MapService } from "./types/Map";
import { GeoApi } from "./geo";
export declare type AspectDict<ED extends EntityDict & BaseEntityDict> = {
operate: <T extends keyof ED, OP extends OperateOption>(params: {
entity: T;
@ -51,19 +53,10 @@ export declare type AspectDict<ED extends EntityDict & BaseEntityDict> = {
getImportationTemplate: (params: {
id: string;
}, context: AsyncContext<ED>) => Promise<ArrayBuffer>;
searchPoi: (options: {
value: string;
areaCode?: string;
indexFrom?: number;
count?: number;
typeCode?: string;
}) => Promise<{
id: string;
areaId: string;
poiName: string;
detail: string;
coordinate: [number, number];
}[]>;
geoService: <ED extends BaseEntityDict & EntityDict, A extends GeoApi>(options: {
api: A;
params: Parameters<MapService[A]>[0];
}, context: AsyncContext<ED>) => Promise<ReturnType<MapService[A]>>;
loadRelations: (params: {
entities: (keyof ED)[];
}, context: AsyncContext<ED>) => Promise<ED['userRelation']['OpSchema'][]>;

26
lib/geo.d.ts vendored
View File

@ -1,12 +1,18 @@
export declare function searchPoi(options: {
value: string;
areaCode?: string;
indexFrom?: number;
count?: number;
typeCode?: string;
}): Promise<{
id: string;
areaId: string;
import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore';
import { EntityDict } from 'oak-domain/lib/types';
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
import { MapService } from './types/Map';
export declare type GeoApi = 'geo' | 'regeo';
export declare function registerGetMapService(fn: <ED extends BaseEntityDict & EntityDict>(context: AsyncContext<ED>) => Promise<MapService>): void;
export declare function geoService<ED extends BaseEntityDict & EntityDict, A extends GeoApi>(options: {
api: A;
params: Parameters<MapService[A]>[0];
}, context: AsyncContext<ED>): Promise<{
poiName: string;
coordinate: [number, number];
areaId: string;
} | {
poiName: string;
areaId: string;
latitude: number;
longitude: number;
}[]>;

View File

@ -1,42 +1,16 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.searchPoi = void 0;
async function searchPoi(options) {
const { value, areaCode, indexFrom, count } = options;
const form = new FormData();
form.set('stName', value);
if (areaCode) {
form.set('code', areaCode);
}
if (indexFrom && count) {
form.set('page', `${indexFrom / count}`);
form.set('size', `${count}`);
}
const result = await fetch('https://dmfw.mca.gov.cn/9095/stname/listPub', {
method: 'post',
body: form,
});
const { records } = await result.json();
const pois = await Promise.all(records.map(async (ele) => {
let { area, standard_name, gdm, id, province_name, city_name, area_name, place_type, } = ele;
// 对返回的area数据进行一些清洗不规范
if (area.length === 9) {
if (area.endsWith('999')) {
area = area.slice(0, 6);
}
}
if (area === '000000') {
// 搜索如长江这样的地理名称时会返回这样的数据,过滤掉
return undefined;
}
return {
id,
areaId: area,
poiName: standard_name,
coordinate: gdm.coordinates[0],
detail: `${province_name}${city_name}${area_name}${standard_name}(${place_type})`,
};
}));
return pois.filter((poi) => !!poi);
exports.geoService = exports.registerGetMapService = void 0;
const assert_1 = require("oak-domain/lib/utils/assert");
let GetMapServiceFn = undefined;
function registerGetMapService(fn) {
GetMapServiceFn = fn;
}
exports.searchPoi = searchPoi;
exports.registerGetMapService = registerGetMapService;
async function geoService(options, context) {
(0, assert_1.assert)(GetMapServiceFn, '未注入指定的GetMapServiceFn');
const service = await GetMapServiceFn(context);
const { api, params } = options;
return await service[api](params);
}
exports.geoService = geoService;

6
lib/index.d.ts vendored
View File

@ -2,7 +2,7 @@ import { operate, select, fetchRows, count, aggregate } from './crud';
import { amap } from './amap';
import { getTranslations } from './locales';
import { registerPorts, clearPorts, importEntity, exportEntity, getImportationTemplate } from './port';
import { searchPoi } from './geo';
import { geoService, registerGetMapService } from './geo';
import { loadRelations } from './relation';
import { crossBridge } from './utils';
declare const aspectDict: {
@ -16,10 +16,10 @@ declare const aspectDict: {
importEntity: typeof importEntity;
exportEntity: typeof exportEntity;
getImportationTemplate: typeof getImportationTemplate;
searchPoi: typeof searchPoi;
geoService: typeof geoService;
loadRelations: typeof loadRelations;
crossBridge: typeof crossBridge;
};
export default aspectDict;
export * from './AspectDict';
export { registerPorts, clearPorts, };
export { registerPorts, clearPorts, registerGetMapService, };

View File

@ -1,6 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.clearPorts = exports.registerPorts = void 0;
exports.registerGetMapService = exports.clearPorts = exports.registerPorts = void 0;
const tslib_1 = require("tslib");
const crud_1 = require("./crud");
const amap_1 = require("./amap");
@ -9,6 +9,7 @@ const port_1 = require("./port");
Object.defineProperty(exports, "registerPorts", { enumerable: true, get: function () { return port_1.registerPorts; } });
Object.defineProperty(exports, "clearPorts", { enumerable: true, get: function () { return port_1.clearPorts; } });
const geo_1 = require("./geo");
Object.defineProperty(exports, "registerGetMapService", { enumerable: true, get: function () { return geo_1.registerGetMapService; } });
const relation_1 = require("./relation");
const utils_1 = require("./utils");
const aspectDict = {
@ -22,7 +23,7 @@ const aspectDict = {
importEntity: port_1.importEntity,
exportEntity: port_1.exportEntity,
getImportationTemplate: port_1.getImportationTemplate,
searchPoi: geo_1.searchPoi,
geoService: geo_1.geoService,
loadRelations: relation_1.loadRelations,
crossBridge: utils_1.crossBridge,
};

26
lib/map/amap.d.ts vendored Normal file
View File

@ -0,0 +1,26 @@
import { MapService } from "../types/Map";
import { AmapKeyType } from 'oak-external-sdk/lib/types/AMap';
export default class AMap implements MapService {
keys: Array<{
key: string;
type: AmapKeyType;
count: number;
}>;
index: number;
constructor(keys: Array<{
key: string;
type: AmapKeyType;
}>);
protected getKey(): string;
regeo(param: {
latitude: number;
longitude: number;
}): Promise<{
poiName: any;
areaId: any;
}>;
geo(param: {
name: string;
areaId?: string;
}): Promise<any>;
}

61
lib/map/amap.js Normal file
View File

@ -0,0 +1,61 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const assert_1 = tslib_1.__importDefault(require("assert"));
const AmapSDK_1 = tslib_1.__importDefault(require("oak-external-sdk/lib/AmapSDK"));
const AMap_1 = require("oak-external-sdk/lib/types/AMap");
class AMap {
keys;
index;
constructor(keys) {
(0, assert_1.default)(keys.length > 0);
this.keys = keys.map(ele => ({
...ele,
count: AMap_1.WebServiceKeyWeight[ele.type],
}));
this.index = 0;
}
getKey() {
while (true) {
const key = this.keys[this.index];
if (key.count > 0) {
key.count--;
return key.key;
}
key.count = AMap_1.WebServiceKeyWeight[key.type];
this.index = (this.index + 1) % this.keys.length;
}
}
async regeo(param) {
const key = this.getKey();
const instance = AmapSDK_1.default.getInstance(key);
/**
* https://lbs.amap.com/api/webservice/guide/api/georegeo
*/
const { regeocode } = await instance.regeo(param);
return {
poiName: regeocode.formatted_address,
areaId: regeocode.addressComponent.towncode,
};
}
;
async geo(param) {
const key = this.getKey();
const instance = AmapSDK_1.default.getInstance(key);
/**
* https://lbs.amap.com/api/webservice/guide/api/georegeo
*/
const { name, areaId } = param;
const { geocodes } = await instance.geocode({
address: name,
city: areaId,
});
return geocodes.map((ele) => ({
poiName: ele.formatted_address,
areaId: ele.adcode,
latitude: ele.location[0],
longitude: ele.location[1],
}));
}
}
exports.default = AMap;

9
lib/port.d.ts vendored
View File

@ -1,21 +1,22 @@
import { EntityDict } from 'oak-domain/lib/types/Entity';
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
import { Importation, Exportation } from 'oak-domain/lib/types/Port';
import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore';
export declare function registerPorts<ED extends EntityDict>(importations: Importation<ED, keyof ED, any, any>[], exportations: Exportation<ED, keyof ED, any, any>[]): void;
export declare function registerPorts<ED extends EntityDict & BaseEntityDict>(importations: Importation<ED, keyof ED, any, any>[], exportations: Exportation<ED, keyof ED, any, any>[]): void;
export declare function clearPorts(): void;
export declare function importEntity<ED extends EntityDict, Cxt extends AsyncContext<ED>>(params: {
export declare function importEntity<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>>(params: {
entity: string;
id: string;
file: any;
option: string;
s2jOpts?: string;
}, context: Cxt): Promise<ArrayBuffer | void>;
export declare function exportEntity<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED>>(params: {
export declare function exportEntity<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED>>(params: {
entity: T;
id: string;
filter?: ED[T]['Selection']['filter'];
properties?: Record<string, any>;
}, context: Cxt): Promise<ArrayBuffer>;
export declare function getImportationTemplate<ED extends EntityDict, Cxt extends AsyncContext<ED>>(params: {
export declare function getImportationTemplate<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>>(params: {
id: string;
}, context: Cxt): Promise<ArrayBuffer>;

18
lib/types/Map.d.ts vendored Normal file
View File

@ -0,0 +1,18 @@
export interface MapService {
regeo: (param: {
latitude: number;
longitude: number;
}) => Promise<{
poiName: string;
areaId: string;
}>;
geo: (param: {
name: string;
areaId?: string;
}) => Promise<Array<{
poiName: string;
areaId: string;
latitude: number;
longitude: number;
}>>;
}

3
lib/types/Map.js Normal file
View File

@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
;

View File

@ -2,6 +2,8 @@ import { EntityDict, OperateOption, SelectOption, OperationResult, AggregationRe
import { AmapInstance } from "oak-external-sdk/lib/service/amap/Amap";
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
import { AsyncContext } from "oak-domain/lib/store/AsyncRowStore";
import { MapService } from "./types/Map";
import { GeoApi } from "./geo";
export type AspectDict<ED extends EntityDict & BaseEntityDict> = {
operate: <T extends keyof ED, OP extends OperateOption>(
@ -69,13 +71,10 @@ export type AspectDict<ED extends EntityDict & BaseEntityDict> = {
properties?: Record<string, any>;
}, context: AsyncContext<ED>) => Promise<ArrayBuffer>;
getImportationTemplate: (params: { id: string }, context: AsyncContext<ED>) => Promise<ArrayBuffer>;
searchPoi: (options: {
value: string;
areaCode?: string;
indexFrom?: number;
count?: number;
typeCode?: string;
}) => Promise<{ id: string; areaId: string; poiName: string; detail: string; coordinate: [number, number] }[]>;
geoService: <ED extends BaseEntityDict & EntityDict, A extends GeoApi>(options: {
api: A;
params: Parameters<MapService[A]>[0];
}, context: AsyncContext<ED>) => Promise<ReturnType<MapService[A]>>;
loadRelations: (params: {
entities: (keyof ED)[],
}, context: AsyncContext<ED>) => Promise<ED['userRelation']['OpSchema'][]>;

View File

@ -1,90 +1,28 @@
import { assert } from 'oak-domain/lib/utils/assert';
import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore';
import {
EntityDict,
} from 'oak-domain/lib/types';
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
import { MapService } from './types/Map';
/**
* poiName搜索地理位置
* @param options
* https://dmfw.mca.gov.cn/interface.html
*/
type DmfwPoi = {
id: string;
area: string;
standard_name: string;
gdm: {
coordinates: [[number, number]];
};
place_type: string;
place_code: string;
area_name: string;
city_name: string;
province_name: string;
};
export type GeoApi = 'geo' | 'regeo';
export async function searchPoi(options: {
value: string;
areaCode?: string;
indexFrom?: number;
count?: number;
typeCode?: string;
}): Promise<
{
id: string;
areaId: string;
poiName: string;
coordinate: [number, number];
}[]
> {
const { value, areaCode, indexFrom, count } = options;
const form = new FormData();
form.set('stName', value);
if (areaCode) {
form.set('code', areaCode);
}
if (indexFrom && count) {
form.set('page', `${indexFrom / count}`);
form.set('size', `${count}`);
}
let GetMapServiceFn: (<ED extends BaseEntityDict & EntityDict, C extends AsyncContext<ED>>(context: C) => Promise<MapService>) | undefined = undefined;
const result = await fetch('https://dmfw.mca.gov.cn/9095/stname/listPub', {
method: 'post',
body: form,
});
const { records } = await result.json();
const pois = await Promise.all(
records.map(async (ele: DmfwPoi) => {
let {
area,
standard_name,
gdm,
id,
province_name,
city_name,
area_name,
place_type,
} = ele;
// 对返回的area数据进行一些清洗不规范
if (area.length === 9) {
if (area.endsWith('999')) {
area = area.slice(0, 6);
}
}
if (area === '000000') {
// 搜索如长江这样的地理名称时会返回这样的数据,过滤掉
return undefined;
}
return {
id,
areaId: area,
poiName: standard_name,
coordinate: gdm.coordinates[0],
detail: `${province_name}${city_name}${area_name}${standard_name}(${place_type})`,
};
})
);
return pois.filter((poi) => !!poi) as {
id: string;
areaId: string;
poiName: string;
coordinate: [number, number];
}[];
export function registerGetMapService(
fn: <ED extends BaseEntityDict & EntityDict>(context: AsyncContext<ED>) => Promise<MapService>
) {
GetMapServiceFn = fn;
}
export async function geoService<ED extends BaseEntityDict & EntityDict, A extends GeoApi>(options: {
api: A;
params: Parameters<MapService[A]>[0];
}, context: AsyncContext<ED>) {
assert(GetMapServiceFn, '未注入指定的GetMapServiceFn');
const service = await GetMapServiceFn<ED, AsyncContext<ED>>(context);
const { api, params } = options;
return await service[api](params as any);
}

View File

@ -2,7 +2,7 @@ import { operate, select, fetchRows, count, aggregate } from './crud';
import { amap } from './amap';
import { getTranslations } from './locales';
import { registerPorts, clearPorts, importEntity, exportEntity, getImportationTemplate } from './port';
import { searchPoi } from './geo';
import { geoService, registerGetMapService } from './geo';
import { loadRelations } from './relation';
import { crossBridge } from './utils';
@ -17,7 +17,7 @@ const aspectDict = {
importEntity,
exportEntity,
getImportationTemplate,
searchPoi,
geoService,
loadRelations,
crossBridge,
};
@ -27,4 +27,5 @@ export * from './AspectDict';
export {
registerPorts,
clearPorts,
registerGetMapService,
};

81
src/map/amap.ts Normal file
View File

@ -0,0 +1,81 @@
import { MapService } from "../types/Map";
import assert from 'assert';
import AmapSDK from 'oak-external-sdk/lib/AmapSDK';
import { AmapKeyType, WebServiceKeyWeight } from 'oak-external-sdk/lib/types/AMap';
export default class AMap implements MapService {
keys: Array<{
key: string;
type: AmapKeyType;
count: number;
}>;
index: number;
constructor(keys: Array<{
key: string;
type: AmapKeyType;
}>) {
assert(keys.length > 0);
this.keys = keys.map(
ele => ({
...ele,
count: WebServiceKeyWeight[ele.type],
})
);
this.index = 0;
}
protected getKey() {
while (true) {
const key = this.keys[this.index];
if (key.count > 0) {
key.count --;
return key.key;
}
key.count = WebServiceKeyWeight[key.type];
this.index = (this.index + 1) % this.keys.length;
}
}
async regeo(param: {
latitude: number;
longitude: number;
}) {
const key = this.getKey();
const instance = AmapSDK.getInstance(key);
/**
* https://lbs.amap.com/api/webservice/guide/api/georegeo
*/
const { regeocode } = await instance.regeo(param)
return {
poiName: regeocode.formatted_address,
areaId: regeocode.addressComponent.towncode,
}
};
async geo(param: { name: string; areaId?: string }) {
const key = this.getKey();
const instance = AmapSDK.getInstance(key);
/**
* https://lbs.amap.com/api/webservice/guide/api/georegeo
*/
const { name, areaId } = param;
const { geocodes } = await instance.geocode({
address: name,
city: areaId,
});
return geocodes.map(
(ele: any) =>({
poiName: ele.formatted_address,
areaId: ele.adcode,
latitude: ele.location[0],
longitude: ele.location[1],
})
);
}
}

View File

@ -1,5 +1,6 @@
import { assert } from 'oak-domain/lib/utils/assert';
import { EntityDict, SelectOption } from 'oak-domain/lib/types/Entity';
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
import { Importation, Exportation } from 'oak-domain/lib/types/Port';
import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore';
import { read, utils, write, readFile, Sheet2JSONOpts } from 'xlsx';
@ -8,7 +9,7 @@ import { read, utils, write, readFile, Sheet2JSONOpts } from 'xlsx';
const Importations: Record<string, any> = {};
const Exportations: Record<string, any> = {};
export function registerPorts<ED extends EntityDict>(
export function registerPorts<ED extends EntityDict & BaseEntityDict>(
importations: Importation<ED, keyof ED, any, any>[],
exportations: Exportation<ED, keyof ED, any, any>[]
) {
@ -42,18 +43,18 @@ export function clearPorts() {
}
}
function getImportation<ED extends EntityDict, T extends keyof ED>(id: string) {
function getImportation<ED extends EntityDict & BaseEntityDict, T extends keyof ED>(id: string) {
assert(Importations.hasOwnProperty(id), `id为[${id}]的importation不存在`);
return Importations[id] as Importation<ED, T, any, any>;
}
function getExportation<ED extends EntityDict, T extends keyof ED>(id: string) {
function getExportation<ED extends EntityDict & BaseEntityDict, T extends keyof ED>(id: string) {
assert(Exportations.hasOwnProperty(id), `id为[${id}]的exportation不存在`);
return Exportations[id] as Exportation<ED, T, any, any>;
}
export async function importEntity<
ED extends EntityDict,
ED extends EntityDict & BaseEntityDict,
Cxt extends AsyncContext<ED>
>(
params: {
@ -110,7 +111,7 @@ export async function importEntity<
}
export async function exportEntity<
ED extends EntityDict,
ED extends EntityDict & BaseEntityDict,
T extends keyof ED,
Cxt extends AsyncContext<ED>
>(
@ -156,7 +157,7 @@ export async function exportEntity<
}
export async function getImportationTemplate<
ED extends EntityDict,
ED extends EntityDict & BaseEntityDict,
Cxt extends AsyncContext<ED>
>(params: { id: string }, context: Cxt): Promise<ArrayBuffer> {
const id = params.id;

25
src/types/Map.ts Normal file
View File

@ -0,0 +1,25 @@
import { EntityDict } from 'oak-domain/lib/base-app-domain';
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity';
import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore';
export interface MapService {
regeo: (param: {
latitude: number;
longitude: number;
}) => Promise<{
poiName: string;
areaId: string;
}>;
geo: (param: {
name: string;
areaId?: string;
}) => Promise<
Array<{
poiName: string;
areaId: string;
latitude: number;
longitude: number;
}>
>;
};