diff --git a/frontend/src/metadata/components/data-process-setter/index.js b/frontend/src/metadata/components/data-process-setter/index.js
index 92add309eb..7d87844e98 100644
--- a/frontend/src/metadata/components/data-process-setter/index.js
+++ b/frontend/src/metadata/components/data-process-setter/index.js
@@ -5,6 +5,7 @@ import SortSetter from './sort-setter';
import GroupbySetter from './groupby-setter';
import PreHideColumnSetter from './pre-hide-column-setter';
import HideColumnSetter from './hide-column-setter';
+import MapTypeSetter from './map-type-setter';
export {
GalleryGroupBySetter,
@@ -14,4 +15,5 @@ export {
GroupbySetter,
PreHideColumnSetter,
HideColumnSetter,
+ MapTypeSetter,
};
diff --git a/frontend/src/metadata/components/data-process-setter/map-type-setter/index.css b/frontend/src/metadata/components/data-process-setter/map-type-setter/index.css
new file mode 100644
index 0000000000..cac45ca19e
--- /dev/null
+++ b/frontend/src/metadata/components/data-process-setter/map-type-setter/index.css
@@ -0,0 +1,57 @@
+.metadata-map-type-setter {
+ width: fit-content;
+ height: 36px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ border: 1px solid #e2e2e2;
+ border-radius: 3px;
+}
+
+.metadata-map-type-setter .metadata-map-type-button {
+ width: 66px;
+ height: 28px;
+ color: #212529;
+ background-color: #fff;
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 0.875rem;
+ border: 0;
+ border-radius: 2px;
+}
+
+.metadata-map-type-setter .metadata-map-type-button:hover {
+ background-color: #f0f0f0;
+ cursor: pointer;
+}
+
+.metadata-map-type-setter .metadata-map-type-button.active {
+ background-color: #f5f5f5;
+}
+
+.metadata-map-type-setter .metadata-map-type-button span {
+ display: block;
+ text-align: center;
+ width: 100%;
+}
+
+.metadata-map-type-button:not(:first-child)::before {
+ content: '';
+ width: 1px;
+ height: 22px;
+ background-color: #e2e2e2;
+ position: absolute;
+ top: 50%;
+ transform: translateY(-50%);
+ transition: opacity 0.3s;
+ left: -1px;
+}
+
+.metadata-map-type-button:hover::before,
+.metadata-map-type-button.active::before,
+.metadata-map-type-button:hover + .metadata-map-type-button::before,
+.metadata-map-type-button.active + .metadata-map-type-button::before {
+ opacity: 0;
+}
\ No newline at end of file
diff --git a/frontend/src/metadata/components/data-process-setter/map-type-setter/index.js b/frontend/src/metadata/components/data-process-setter/map-type-setter/index.js
new file mode 100644
index 0000000000..8fe2cb1ba5
--- /dev/null
+++ b/frontend/src/metadata/components/data-process-setter/map-type-setter/index.js
@@ -0,0 +1,49 @@
+import React, { useState, useCallback, useEffect } from 'react';
+import PropTypes from 'prop-types';
+import classnames from 'classnames';
+import { EVENT_BUS_TYPE, MAP_TYPE } from '../../../constants';
+import { gettext } from '../../../../utils/constants';
+
+import './index.css';
+
+const TYPE_MAP = {
+ [MAP_TYPE.MAP]: gettext('Map'),
+ [MAP_TYPE.SATELLITE]: gettext('Satellite')
+};
+
+const MapTypeSetter = ({ view }) => {
+ const [currentType, setCurrentType] = useState(MAP_TYPE.MAP);
+
+ useEffect(() => {
+ const savedValue = window.sfMetadataContext.localStorage.getItem('map-type', MAP_TYPE.MAP);
+ setCurrentType(savedValue || MAP_TYPE.MAP);
+ }, [view?._id]);
+
+ const handleTypeChange = useCallback((newType) => {
+ setCurrentType(newType);
+ window.sfMetadataContext.localStorage.setItem('map-type', newType);
+ window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.SWITCH_MAP_TYPE, newType);
+ }, []);
+
+ return (
+
+ {Object.entries(TYPE_MAP).map(([type, label]) => (
+
+ ))}
+
+ );
+};
+
+MapTypeSetter.propTypes = {
+ view: PropTypes.shape({
+ _id: PropTypes.string
+ })
+};
+
+export default MapTypeSetter;
diff --git a/frontend/src/metadata/components/view-toolbar/map-view-toolbar/index.js b/frontend/src/metadata/components/view-toolbar/map-view-toolbar/index.js
index 4be657a74f..8d2e0721f4 100644
--- a/frontend/src/metadata/components/view-toolbar/map-view-toolbar/index.js
+++ b/frontend/src/metadata/components/view-toolbar/map-view-toolbar/index.js
@@ -1,7 +1,7 @@
import React, { useMemo } from 'react';
import PropTypes from 'prop-types';
import { PRIVATE_COLUMN_KEY } from '../../../constants';
-import { FilterSetter } from '../../data-process-setter';
+import { FilterSetter, MapTypeSetter } from '../../data-process-setter';
const MapViewToolBar = ({
readOnly,
@@ -22,6 +22,7 @@ const MapViewToolBar = ({
return (
<>
+
{
}, [repoID, metadata]);
const addMapController = useCallback(() => {
- var navigation = new window.BMap.NavigationControl();
+ const ZoomControl = createBMapZoomControl(window.BMap);
+ const zoomControl = new ZoomControl();
const GeolocationControl = createBMapGeolocationControl(window.BMap, (err, point) => {
if (!err && point) {
mapRef.current.setCenter({ lng: point.lng, lat: point.lat });
}
});
+
const geolocationControl = new GeolocationControl();
+
+ mapRef.current.addControl(zoomControl);
mapRef.current.addControl(geolocationControl);
- mapRef.current.addControl(navigation);
}, []);
const renderMarkersBatch = useCallback(() => {
@@ -121,6 +125,16 @@ const Map = () => {
);
}, []);
+ const getMapType = useCallback((type) => {
+ if (!mapRef.current) return;
+ switch (type) {
+ case MAP_TYPE.SATELLITE:
+ return window.BMAP_SATELLITE_MAP;
+ default:
+ return window.BMAP_NORMAL_MAP;
+ }
+ }, []);
+
const renderBaiduMap = useCallback(() => {
setIsLoading(false);
if (!window.BMap.Map) return;
@@ -142,6 +156,8 @@ const Map = () => {
const point = new window.BMap.Point(lng, lat);
mapRef.current.centerAndZoom(point, DEFAULT_ZOOM);
mapRef.current.enableScrollWheelZoom(true);
+ // const type = window.sfMetadataContext.localStorage.getItem('map-type');
+ // mapRef.current.setMapType(getMapType(type));
addMapController();
initializeUserMarker();
@@ -152,7 +168,19 @@ const Map = () => {
}, [addMapController, initializeClusterer, initializeUserMarker, renderMarkersBatch]);
useEffect(() => {
- if (mapInfo.type === MAP_TYPE.B_MAP) {
+ const switchMapTypeSubscribe = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.SWITCH_MAP_TYPE, (newType) => {
+ window.sfMetadataContext.localStorage.setItem('map-type', newType);
+ mapRef.current && mapRef.current.setMapType(getMapType(newType));
+ });
+
+ return () => {
+ switchMapTypeSubscribe();
+ };
+
+ }, [getMapType]);
+
+ useEffect(() => {
+ if (mapInfo.type === MAP_PROVIDER.B_MAP) {
window.renderMap = renderBaiduMap;
loadBMap(mapInfo.key).then(() => renderBaiduMap());
return () => {
diff --git a/frontend/src/metadata/views/map/zoom-control.js b/frontend/src/metadata/views/map/zoom-control.js
new file mode 100644
index 0000000000..3600ba85ff
--- /dev/null
+++ b/frontend/src/metadata/views/map/zoom-control.js
@@ -0,0 +1,65 @@
+import { Utils } from '../../../utils/utils';
+
+export function createBMapZoomControl(BMap, callback) {
+ function ZoomControl() {
+ this.defaultAnchor = window.BMAP_ANCHOR_BOTTOM_RIGHT;
+ this.defaultOffset = new BMap.Size(80, Utils.isDesktop() ? 30 : 90);
+ }
+ ZoomControl.prototype = new window.BMap.Control();
+ ZoomControl.prototype.initialize = function (map) {
+ const div = document.createElement('div');
+ div.className = 'sf-BMap-zoom-control';
+ div.style = 'display: flex; justify-content: center; align-items: center;';
+
+ const zoomInButton = document.createElement('button');
+ zoomInButton.className = 'sf-BMap-zoom-button';
+ zoomInButton.style = 'display: flex; justify-content: center; align-items: center;';
+ zoomInButton.innerHTML = '';
+ div.appendChild(zoomInButton);
+
+ const divider = document.createElement('div');
+ divider.style = 'height: 22px; width: 1px; background-color: #ccc;';
+ div.appendChild(divider);
+
+ const zoomOutButton = document.createElement('button');
+ zoomOutButton.className = 'sf-BMap-zoom-button';
+ zoomOutButton.style = 'display: flex; justify-content: center; align-items: center;';
+ zoomOutButton.innerHTML = '';
+ div.appendChild(zoomOutButton);
+
+ if (Utils.isDesktop()) {
+ setNodeStyle(div, 'height: 40px; width: 111px; line-height: 40px');
+ } else {
+ setNodeStyle(div, 'height: 35px; width: 80px; line-height: 35px; opacity: 0.75');
+ }
+
+ const updateButtonStates = () => {
+ const zoomLevel = map.getZoom();
+ const maxZoom = map.getMaxZoom();
+ const minZoom = map.getMinZoom();
+
+ zoomInButton.disabled = zoomLevel >= maxZoom;
+ zoomOutButton.disabled = zoomLevel <= minZoom;
+ };
+
+ zoomInButton.onclick = (e) => {
+ e.preventDefault();
+ map.zoomTo(map.getZoom() + 2);
+ };
+
+ zoomOutButton.onclick = (e) => {
+ e.preventDefault();
+ map.zoomTo(map.getZoom() - 2);
+ };
+
+ map.addEventListener('zoomend', updateButtonStates);
+ map.getContainer().appendChild(div);
+ return div;
+ };
+
+ return ZoomControl;
+}
+
+function setNodeStyle(dom, styleText) {
+ dom.style.cssText += styleText;
+}