1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-05-12 18:05:05 +00:00
seahub/frontend/src/metadata/views/map/map-view/index.js
2025-01-10 14:02:03 +08:00

185 lines
6.6 KiB
JavaScript

import React, { useCallback, useEffect, useMemo, useRef } from 'react';
import PropTypes from 'prop-types';
import loadBMap, { initMapInfo } from '../../../../utils/map-utils';
import { appAvatarURL, baiduMapKey, googleMapKey, mediaUrl } from '../../../../utils/constants';
import { isValidPosition } from '../../../utils/validate';
import { wgs84_to_gcj02, gcj02_to_bd09 } from '../../../../utils/coord-transform';
import { MAP_TYPE as MAP_PROVIDER } from '../../../../constants';
import { EVENT_BUS_TYPE, MAP_TYPE, STORAGE_MAP_CENTER_KEY, STORAGE_MAP_TYPE_KEY, STORAGE_MAP_ZOOM_KEY } from '../../../constants';
import { createBMapGeolocationControl, createBMapZoomControl } from './control';
import { customAvatarOverlay, customImageOverlay } from './overlay';
import './index.css';
const DEFAULT_POSITION = { lng: 104.195, lat: 35.861 };
const DEFAULT_ZOOM = 4;
const BATCH_SIZE = 500;
const MAX_ZOOM = 21;
const MIN_ZOOM = 3;
const MapView = ({ images, onOpenCluster }) => {
const mapInfo = useMemo(() => initMapInfo({ baiduMapKey, googleMapKey }), []);
const mapRef = useRef(null);
const clusterRef = useRef(null);
const batchIndexRef = useRef(0);
const saveMapState = useCallback(() => {
if (!mapRef.current) return;
const point = mapRef.current.getCenter && mapRef.current.getCenter();
const zoom = mapRef.current.getZoom && mapRef.current.getZoom();
window.sfMetadataContext.localStorage.setItem(STORAGE_MAP_CENTER_KEY, point);
window.sfMetadataContext.localStorage.setItem(STORAGE_MAP_ZOOM_KEY, zoom);
}, []);
const addMapController = useCallback(() => {
const ZoomControl = createBMapZoomControl(window.BMapGL, { maxZoom: MAX_ZOOM, minZoom: MIN_ZOOM }, saveMapState);
const zoomControl = new ZoomControl();
const GeolocationControl = createBMapGeolocationControl(window.BMapGL, (point) => {
point && mapRef.current && mapRef.current.setCenter(point);
});
const geolocationControl = new GeolocationControl();
mapRef.current.addControl(zoomControl);
mapRef.current.addControl(geolocationControl);
}, [saveMapState]);
const initializeUserMarker = useCallback((centerPoint) => {
if (!window.BMapGL || !mapRef.current) return;
const imageUrl = `${mediaUrl}img/marker.png`;
const avatarMarker = customAvatarOverlay(centerPoint, appAvatarURL, imageUrl);
mapRef.current.addOverlay(avatarMarker);
}, []);
const getBMapType = useCallback((type) => {
switch (type) {
case MAP_TYPE.SATELLITE: {
return window.BMAP_EARTH_MAP;
}
default: {
return window.BMAP_NORMAL_MAP;
}
}
}, []);
const loadMapState = useCallback(() => {
const savedCenter = window.sfMetadataContext.localStorage.getItem(STORAGE_MAP_CENTER_KEY) || DEFAULT_POSITION;
const savedZoom = window.sfMetadataContext.localStorage.getItem(STORAGE_MAP_ZOOM_KEY) || DEFAULT_ZOOM;
return { center: savedCenter, zoom: savedZoom };
}, []);
const onClickMarker = useCallback((e, markers) => {
saveMapState();
const imageIds = markers.map(marker => marker._id);
onOpenCluster(imageIds);
}, [onOpenCluster, saveMapState]);
const renderMarkersBatch = useCallback(() => {
if (!images.length || !clusterRef.current) return;
const startIndex = batchIndexRef.current * BATCH_SIZE;
const endIndex = Math.min(startIndex + BATCH_SIZE, images.length);
const batchMarkers = [];
for (let i = startIndex; i < endIndex; i++) {
const image = images[i];
const { lng, lat } = image;
const point = new window.BMapGL.Point(lng, lat);
const marker = customImageOverlay(point, image, {
callback: (e, markers) => onClickMarker(e, markers)
});
batchMarkers.push(marker);
}
clusterRef.current.addMarkers(batchMarkers);
if (endIndex < images.length) {
batchIndexRef.current += 1;
setTimeout(renderMarkersBatch, 20); // Schedule the next batch
}
}, [images, onClickMarker]);
const initializeCluster = useCallback(() => {
if (mapRef.current && !clusterRef.current) {
clusterRef.current = new window.BMapLib.MarkerCluster(mapRef.current, {
callback: (e, markers) => onClickMarker(e, markers),
maxZoom: 21,
});
}
}, [onClickMarker]);
const renderBaiduMap = useCallback(() => {
if (!mapRef.current || !window.BMapGL.Map) return;
let { center, zoom } = loadMapState();
let userPosition = { lng: 116.40396418840683, lat: 39.915106021711345 };
// ask for user location
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition((userInfo) => {
const gcPosition = wgs84_to_gcj02(userInfo.coords.longitude, userInfo.coords.latitude);
const bdPosition = gcj02_to_bd09(gcPosition.lng, gcPosition.lat);
const { lng, lat } = bdPosition;
userPosition = new window.BMapGL.Point(lng, lat);
center = userPosition;
window.sfMetadataContext.localStorage.setItem(STORAGE_MAP_CENTER_KEY, center);
});
}
const mapTypeValue = window.sfMetadataContext.localStorage.getItem(STORAGE_MAP_TYPE_KEY);
mapRef.current = new window.BMapGL.Map('sf-metadata-map-container', {
enableMapClick: false,
minZoom: MIN_ZOOM,
maxZoom: MAX_ZOOM,
mapType: getBMapType(mapTypeValue),
});
if (isValidPosition(center?.lng, center?.lat)) {
mapRef.current.centerAndZoom(center, zoom);
}
mapRef.current.enableScrollWheelZoom(true);
addMapController();
initializeUserMarker(userPosition);
initializeCluster();
batchIndexRef.current = 0;
renderMarkersBatch();
}, [addMapController, initializeCluster, initializeUserMarker, renderMarkersBatch, getBMapType, loadMapState]);
useEffect(() => {
const modifyMapTypeSubscribe = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_MAP_TYPE, (newType) => {
window.sfMetadataContext.localStorage.setItem(STORAGE_MAP_TYPE_KEY, newType);
const mapType = getBMapType(newType);
mapRef.current && mapRef.current.setMapType(mapType);
});
return () => {
modifyMapTypeSubscribe();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
if (mapInfo.type === MAP_PROVIDER.B_MAP) {
loadBMap(mapInfo.key).then(() => renderBaiduMap());
return () => {
window.renderMap = null;
};
}
return;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div className="sf-metadata-view-map">
<div className="sf-metadata-map-container" ref={mapRef} id="sf-metadata-map-container"></div>
</div>
);
};
MapView.propTypes = {
images: PropTypes.array,
onOpenCluster: PropTypes.func,
};
export default MapView;