|
|
@@ -0,0 +1,168 @@
|
|
|
+import type { MapLocationPoint, MapPolygon, MapPolyline } from '@/types/Map';
|
|
|
+
|
|
|
+/** GeoJSON 坐标 [经度, 纬度(, 可选高度)] */
|
|
|
+export type GeoJsonPosition = [number, number] | [number, number, number];
|
|
|
+
|
|
|
+export type GeoJsonGeometry =
|
|
|
+ | { type: 'Polygon'; coordinates: GeoJsonPosition[][] }
|
|
|
+ | { type: 'MultiPolygon'; coordinates: GeoJsonPosition[][][] }
|
|
|
+ | { type: 'LineString'; coordinates: GeoJsonPosition[] }
|
|
|
+ | { type: 'MultiLineString'; coordinates: GeoJsonPosition[][] }
|
|
|
+ | { type: 'GeometryCollection'; geometries: GeoJsonGeometry[] }
|
|
|
+ | { type: string; coordinates?: unknown; geometries?: unknown };
|
|
|
+
|
|
|
+export interface GeoJsonFeature {
|
|
|
+ type: 'Feature';
|
|
|
+ properties?: Record<string, unknown> | null;
|
|
|
+ geometry: GeoJsonGeometry | null;
|
|
|
+}
|
|
|
+
|
|
|
+export interface GeoJsonFeatureCollection {
|
|
|
+ type: 'FeatureCollection';
|
|
|
+ features: GeoJsonFeature[];
|
|
|
+}
|
|
|
+
|
|
|
+export interface GeoJsonToWechatMapOptions {
|
|
|
+ fillColor?: string;
|
|
|
+ strokeColor?: string;
|
|
|
+ strokeWidth?: number;
|
|
|
+ /** LineString / MultiLineString 使用的颜色 */
|
|
|
+ lineColor?: string;
|
|
|
+ lineWidth?: number;
|
|
|
+ level?: MapPolygon['level'];
|
|
|
+ /** 仅转换满足的要素(例如按 adcode 过滤) */
|
|
|
+ featureFilter?: (feature: GeoJsonFeature) => boolean;
|
|
|
+}
|
|
|
+
|
|
|
+const defaultOptions: Required<
|
|
|
+ Pick<
|
|
|
+ GeoJsonToWechatMapOptions,
|
|
|
+ 'fillColor' | 'strokeColor' | 'strokeWidth' | 'lineColor' | 'lineWidth' | 'level'
|
|
|
+ >
|
|
|
+> = {
|
|
|
+ fillColor: '#3388ff33',
|
|
|
+ strokeColor: '#3388ff',
|
|
|
+ strokeWidth: 1,
|
|
|
+ lineColor: '#3388ff',
|
|
|
+ lineWidth: 2,
|
|
|
+ level: 'abovelabels',
|
|
|
+};
|
|
|
+
|
|
|
+function lngLatToPoint(lngLat: GeoJsonPosition): MapLocationPoint {
|
|
|
+ return { longitude: lngLat[0], latitude: lngLat[1] };
|
|
|
+}
|
|
|
+
|
|
|
+/** GeoJSON 多边形环通常为闭合环;若未闭合则补全,满足 map 组件闭合多边形要求 */
|
|
|
+function ringToClosedPolygonPoints(ring: GeoJsonPosition[]): MapLocationPoint[] {
|
|
|
+ if (ring.length < 3) return [];
|
|
|
+ const pts = ring.map(lngLatToPoint);
|
|
|
+ const first = pts[0];
|
|
|
+ const last = pts[pts.length - 1];
|
|
|
+ if (first.longitude !== last.longitude || first.latitude !== last.latitude) {
|
|
|
+ pts.push({ latitude: first.latitude, longitude: first.longitude });
|
|
|
+ }
|
|
|
+ return pts;
|
|
|
+}
|
|
|
+
|
|
|
+function appendPolygonOuterRing(
|
|
|
+ rings: GeoJsonPosition[][] | undefined,
|
|
|
+ o: typeof defaultOptions,
|
|
|
+ polygons: MapPolygon[]
|
|
|
+): void {
|
|
|
+ if (!rings?.length) return;
|
|
|
+ const outer = rings[0];
|
|
|
+ const points = ringToClosedPolygonPoints(outer);
|
|
|
+ if (points.length < 4) return;
|
|
|
+ polygons.push({
|
|
|
+ points,
|
|
|
+ fillColor: o.fillColor,
|
|
|
+ strokeColor: o.strokeColor,
|
|
|
+ strokeWidth: o.strokeWidth,
|
|
|
+ level: o.level,
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+function appendLine(coords: GeoJsonPosition[] | undefined, o: typeof defaultOptions, polylines: MapPolyline[]): void {
|
|
|
+ if (!coords?.length) return;
|
|
|
+ const points = coords.map(lngLatToPoint);
|
|
|
+ if (points.length < 2) return;
|
|
|
+ polylines.push({
|
|
|
+ points,
|
|
|
+ color: o.lineColor,
|
|
|
+ width: o.lineWidth,
|
|
|
+ level: o.level,
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+function appendFromGeometry(geometry: GeoJsonGeometry | null, o: typeof defaultOptions, polygons: MapPolygon[], polylines: MapPolyline[]): void {
|
|
|
+ if (!geometry) return;
|
|
|
+ switch (geometry.type) {
|
|
|
+ case 'Polygon':
|
|
|
+ appendPolygonOuterRing(geometry.coordinates as GeoJsonPosition[][], o, polygons);
|
|
|
+ break;
|
|
|
+ case 'MultiPolygon':
|
|
|
+ for (const polygonCoords of (geometry.coordinates as GeoJsonPosition[][][])) {
|
|
|
+ appendPolygonOuterRing(polygonCoords, o, polygons);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case 'LineString':
|
|
|
+ appendLine(geometry.coordinates as GeoJsonPosition[], o, polylines);
|
|
|
+ break;
|
|
|
+ case 'MultiLineString':
|
|
|
+ for (const line of (geometry.coordinates as GeoJsonPosition[][])) {
|
|
|
+ appendLine(line as GeoJsonPosition[], o, polylines);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case 'GeometryCollection':
|
|
|
+ for (const g of (geometry.geometries as GeoJsonGeometry[])) {
|
|
|
+ appendFromGeometry(g, o, polygons, polylines);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function mergeOptions(options?: GeoJsonToWechatMapOptions): typeof defaultOptions {
|
|
|
+ return {
|
|
|
+ ...defaultOptions,
|
|
|
+ ...options,
|
|
|
+ level: options?.level ?? defaultOptions.level,
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 将 GeoJSON(FeatureCollection 或单个 Feature)转为微信小程序 map 的 polygons / polyline 数据源。
|
|
|
+ * - 坐标系:与数据一致(国内区划数据多为 GCJ-02,与微信 map 一致)。
|
|
|
+ * - GeoJSON 坐标顺序为 [longitude, latitude]。
|
|
|
+ * - Polygon / MultiPolygon:仅取外环填充并描边;洞内环(hole)小程序 map 无法用单 polygon 表达,当前版本忽略。
|
|
|
+ */
|
|
|
+export function geoJsonToWechatMapShapes(
|
|
|
+ data: unknown,
|
|
|
+ options?: GeoJsonToWechatMapOptions
|
|
|
+): { polygons: MapPolygon[]; polylines: MapPolyline[] } {
|
|
|
+ const o = mergeOptions(options);
|
|
|
+ const polygons: MapPolygon[] = [];
|
|
|
+ const polylines: MapPolyline[] = [];
|
|
|
+
|
|
|
+ if (!data || typeof data !== 'object') {
|
|
|
+ return { polygons, polylines };
|
|
|
+ }
|
|
|
+
|
|
|
+ const root = data as Record<string, unknown>;
|
|
|
+
|
|
|
+ if (root.type === 'FeatureCollection' && Array.isArray(root.features)) {
|
|
|
+ for (const f of root.features as GeoJsonFeature[]) {
|
|
|
+ if (options?.featureFilter && !options.featureFilter(f)) continue;
|
|
|
+ appendFromGeometry(f.geometry, o, polygons, polylines);
|
|
|
+ }
|
|
|
+ } else if (root.type === 'Feature') {
|
|
|
+ const f = root as unknown as GeoJsonFeature;
|
|
|
+ if (options?.featureFilter && !options.featureFilter(f)) {
|
|
|
+ return { polygons, polylines };
|
|
|
+ }
|
|
|
+ appendFromGeometry(f.geometry, o, polygons, polylines);
|
|
|
+ }
|
|
|
+
|
|
|
+ return { polygons, polylines };
|
|
|
+}
|