mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-24 04:48:03 +00:00
Feature/google map view (#7828)
* custom overlay * google map clusterer * google controls * update google map in details * fix bug - image dialog does not work after add filters --------- Co-authored-by: zhouwenxuan <aries@Mac.local>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import Icon from '../../icon';
|
||||
import TextTranslation from '../../../utils/text-translation';
|
||||
import { baiduMapKey, gettext } from '../../../utils/constants';
|
||||
import { baiduMapKey, gettext, googleMapKey } from '../../../utils/constants';
|
||||
import { VIEW_TYPE, VIEW_TYPE_ICON } from '../../../metadata/constants';
|
||||
|
||||
export const KEY_ADD_VIEW_MAP = {
|
||||
@@ -46,7 +46,7 @@ export const getNewViewSubMenu = () => {
|
||||
const options = [...ADD_VIEW_OPTIONS];
|
||||
const hasMapOption = options.some(opt => opt.type === VIEW_TYPE.MAP);
|
||||
|
||||
if (!hasMapOption && baiduMapKey) {
|
||||
if (!hasMapOption && (baiduMapKey || googleMapKey)) {
|
||||
options.push({
|
||||
key: KEY_ADD_VIEW_MAP.ADD_MAP,
|
||||
type: VIEW_TYPE.MAP,
|
||||
|
@@ -1,12 +1,41 @@
|
||||
import classnames from 'classnames';
|
||||
import { Utils } from '../../../utils/utils';
|
||||
import { wgs84_to_gcj02 } from '../../../utils/coord-transform';
|
||||
|
||||
export function createBMapGeolocationControl(BMapGL, callback) {
|
||||
export const createGeolocationControl = (map) => {
|
||||
const container = document.createElement('div');
|
||||
container.className = classnames(
|
||||
'sf-map-control-container sf-map-geolocation-control-container d-flex align-items-center justify-content-center',
|
||||
{ 'sf-map-geolocation-control-mobile': !Utils.isDesktop() }
|
||||
);
|
||||
|
||||
const button = document.createElement('div');
|
||||
button.className = 'sf-map-control sf-map-geolocation-control d-flex align-items-center justify-content-center';
|
||||
button.innerHTML = '<i class="sf-map-control-icon sf3-font sf3-font-current-location"></i>';
|
||||
container.appendChild(button);
|
||||
|
||||
container.addEventListener('click', async (e) => {
|
||||
e.preventDefault();
|
||||
const originalClass = container.className;
|
||||
container.className = classnames(originalClass, 'sf-map-control-loading');
|
||||
|
||||
if (navigator.geolocation) {
|
||||
navigator.geolocation.getCurrentPosition((userInfo) => {
|
||||
const gcPosition = wgs84_to_gcj02(userInfo.coords.longitude, userInfo.coords.latitude);
|
||||
map.setCenter(gcPosition);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return container;
|
||||
};
|
||||
|
||||
export function createBMapGeolocationControl({ anchor, offset, callback }) {
|
||||
function GeolocationControl() {
|
||||
this.defaultAnchor = window.BMAP_ANCHOR_BOTTOM_RIGHT;
|
||||
this.defaultOffset = new BMapGL.Size(30, Utils.isDesktop() ? 30 : 90);
|
||||
this.defaultAnchor = anchor || window.BMAP_ANCHOR_BOTTOM_RIGHT;
|
||||
this.defaultOffset = new window.BMapGL.Size(offset?.x || 30, offset?.y || 90);
|
||||
}
|
||||
GeolocationControl.prototype = new BMapGL.Control();
|
||||
GeolocationControl.prototype = new window.BMapGL.Control();
|
||||
GeolocationControl.prototype.initialize = function (map) {
|
||||
const div = document.createElement('div');
|
||||
let className = classnames('sf-map-control-container sf-map-geolocation-control-container d-flex align-items-center justify-content-center', {
|
||||
@@ -21,7 +50,7 @@ export function createBMapGeolocationControl(BMapGL, callback) {
|
||||
div.className = className;
|
||||
div.onclick = (e) => {
|
||||
e.preventDefault();
|
||||
const geolocation = new BMapGL.Geolocation();
|
||||
const geolocation = new window.BMapGL.Geolocation();
|
||||
div.className = classnames(className, 'sf-map-control-loading');
|
||||
geolocation.getCurrentPosition((result) => {
|
||||
div.className = className;
|
||||
|
@@ -37,11 +37,21 @@
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
.sf-map-control-container.sf-map-geolocation-control-container {
|
||||
right: 30px !important;
|
||||
bottom: 30px !important;
|
||||
}
|
||||
|
||||
.sf-map-control-container .sf-map-geolocation-control {
|
||||
width: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.sf-map-control-container.sf-map-zoom-control-container {
|
||||
right: 66px !important;
|
||||
bottom: 30px !important;
|
||||
}
|
||||
|
||||
.sf-map-control-container .sf-map-zoom-control {
|
||||
width: 40px;
|
||||
}
|
||||
@@ -64,3 +74,12 @@
|
||||
.sf-map-control-container .sf-map-control.disabled {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.sf-map-control-container.sf-map-geolocation-control-container {
|
||||
bottom: 90px !important;
|
||||
}
|
||||
.sf-map-control-container.sf-map-zoom-control-container {
|
||||
bottom: 90px !important;
|
||||
}
|
||||
}
|
||||
|
@@ -1,56 +1,97 @@
|
||||
import classnames from 'classnames';
|
||||
import { Utils } from '../../../utils/utils';
|
||||
import { MIN_ZOOM, MAX_ZOOM } from '../../constants/view/map';
|
||||
|
||||
export function createBMapZoomControl(BMapGL, { maxZoom, minZoom, offset }, callback) {
|
||||
const buttonClassName = 'sf-map-control sf-map-zoom-control d-flex align-items-center justify-content-center';
|
||||
|
||||
const createZoomContainer = () => {
|
||||
const container = document.createElement('div');
|
||||
container.className = classnames(
|
||||
'sf-map-control-container sf-map-zoom-control-container d-flex align-items-center justify-content-center',
|
||||
{ 'sf-map-control-container-mobile': !Utils.isDesktop() }
|
||||
);
|
||||
return container;
|
||||
};
|
||||
|
||||
const createButton = (innerHTML) => {
|
||||
const button = document.createElement('div');
|
||||
button.className = 'sf-map-control sf-map-zoom-control d-flex align-items-center justify-content-center';
|
||||
button.innerHTML = innerHTML;
|
||||
return button;
|
||||
};
|
||||
|
||||
const createDivider = () => {
|
||||
const divider = document.createElement('div');
|
||||
divider.className = 'sf-map-control-divider';
|
||||
return divider;
|
||||
};
|
||||
|
||||
const updateButtonStates = (map, zoomIn, zoomOut) => {
|
||||
const zoomLevel = map.getZoom();
|
||||
zoomIn.className = classnames(buttonClassName, { 'disabled': zoomLevel >= MAX_ZOOM });
|
||||
zoomOut.className = classnames(buttonClassName, { 'disabled': zoomLevel <= MIN_ZOOM });
|
||||
};
|
||||
|
||||
export const createZoomControl = (map) => {
|
||||
const container = createZoomContainer();
|
||||
|
||||
const zoomInButton = createButton('<i class="sf-map-control-icon sf3-font sf3-font-zoom-in"></i>');
|
||||
const divider = createDivider();
|
||||
const zoomOutButton = createButton('<i class="sf-map-control-icon sf3-font sf3-font-zoom-out"></i>');
|
||||
|
||||
container.appendChild(zoomInButton);
|
||||
container.appendChild(divider);
|
||||
container.appendChild(zoomOutButton);
|
||||
|
||||
zoomInButton.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
const nextZoom = Math.min(map.getZoom() + 1, MAX_ZOOM);
|
||||
map.setZoom(nextZoom);
|
||||
});
|
||||
|
||||
zoomOutButton.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
const nextZoom = Math.max(map.getZoom() - 1, MIN_ZOOM);
|
||||
map.setZoom(nextZoom);
|
||||
});
|
||||
|
||||
map.addListener('zoom_changed', () => updateButtonStates(map, zoomInButton, zoomOutButton));
|
||||
|
||||
return container;
|
||||
};
|
||||
|
||||
export function createBMapZoomControl(anchor, offset) {
|
||||
function ZoomControl() {
|
||||
this.defaultAnchor = window.BMAP_ANCHOR_BOTTOM_RIGHT;
|
||||
this.defaultOffset = new BMapGL.Size(offset.x, offset.y);
|
||||
this.defaultAnchor = anchor || window.BMAP_ANCHOR_BOTTOM_RIGHT;
|
||||
this.defaultOffset = new window.BMapGL.Size(offset?.x || 66, offset?.y || 30);
|
||||
}
|
||||
ZoomControl.prototype = new BMapGL.Control();
|
||||
ZoomControl.prototype = new window.BMapGL.Control();
|
||||
ZoomControl.prototype.initialize = function (map) {
|
||||
const zoomLevel = map.getZoom();
|
||||
const div = document.createElement('div');
|
||||
div.className = classnames('sf-map-control-container sf-map-zoom-control-container d-flex align-items-center justify-content-center', {
|
||||
'sf-map-control-container-mobile': !Utils.isDesktop()
|
||||
});
|
||||
const container = createZoomContainer();
|
||||
|
||||
const buttonClassName = 'sf-map-control sf-map-zoom-control d-flex align-items-center justify-content-center';
|
||||
const zoomInButton = document.createElement('div');
|
||||
zoomInButton.className = classnames(buttonClassName, { 'disabled': zoomLevel >= maxZoom });
|
||||
zoomInButton.innerHTML = '<i class="sf-map-control-icon sf3-font sf3-font-zoom-in"></i>';
|
||||
div.appendChild(zoomInButton);
|
||||
const zoomInButton = createButton('<i class="sf-map-control-icon sf3-font sf3-font-zoom-in"></i>');
|
||||
const divider = createDivider();
|
||||
const zoomOutButton = createButton('<i class="sf-map-control-icon sf3-font sf3-font-zoom-out"></i>');
|
||||
|
||||
const divider = document.createElement('div');
|
||||
divider.className = 'sf-map-control-divider';
|
||||
div.appendChild(divider);
|
||||
|
||||
const zoomOutButton = document.createElement('div');
|
||||
zoomOutButton.className = classnames(buttonClassName, { 'disabled': zoomLevel <= minZoom });
|
||||
zoomOutButton.innerHTML = '<i class="sf-map-control-icon sf3-font sf3-font-zoom-out"></i>';
|
||||
div.appendChild(zoomOutButton);
|
||||
|
||||
const updateButtonStates = () => {
|
||||
const zoomLevel = map.getZoom();
|
||||
zoomInButton.className = classnames(buttonClassName, { 'disabled': zoomLevel >= maxZoom });
|
||||
zoomOutButton.className = classnames(buttonClassName, { 'disabled': zoomLevel <= minZoom });
|
||||
callback && callback(zoomLevel);
|
||||
};
|
||||
container.appendChild(zoomInButton);
|
||||
container.appendChild(divider);
|
||||
container.appendChild(zoomOutButton);
|
||||
|
||||
zoomInButton.onclick = (e) => {
|
||||
e.preventDefault();
|
||||
const nextZoom = map.getZoom() + 1;
|
||||
map.zoomTo(Math.min(nextZoom, maxZoom));
|
||||
map.zoomTo(Math.min(nextZoom, MAX_ZOOM));
|
||||
};
|
||||
|
||||
zoomOutButton.onclick = (e) => {
|
||||
e.preventDefault();
|
||||
const nextZoom = map.getZoom() - 1;
|
||||
map.zoomTo(Math.max(nextZoom, minZoom));
|
||||
map.zoomTo(Math.max(nextZoom, MIN_ZOOM));
|
||||
};
|
||||
|
||||
map.addEventListener('zoomend', updateButtonStates);
|
||||
map.getContainer().appendChild(div);
|
||||
return div;
|
||||
map.addEventListener('zoomend', () => updateButtonStates(map, zoomInButton, zoomOutButton));
|
||||
map.getContainer().appendChild(container);
|
||||
return container;
|
||||
};
|
||||
|
||||
return ZoomControl;
|
||||
|
@@ -27,3 +27,8 @@
|
||||
.dirent-detail-item-value-map .sf-map-control-container .sf-map-control-divider::before {
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.dirent-detail-item-value-map .sf-map-control-container.sf-map-zoom-control-container {
|
||||
right: 10px !important;
|
||||
bottom: 16px !important;
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@ import { getColumnDisplayName } from '../../../utils/column';
|
||||
import { createBMapZoomControl } from '../../map-controller';
|
||||
import { Utils } from '../../../../utils/utils';
|
||||
import { eventBus } from '../../../../components/common/event-bus';
|
||||
import { createZoomControl } from '../../map-controller/zoom';
|
||||
|
||||
import './index.css';
|
||||
|
||||
@@ -30,7 +31,6 @@ class Location extends React.Component {
|
||||
this.mapType = type;
|
||||
this.mapKey = key;
|
||||
this.map = null;
|
||||
this.currentPosition = {};
|
||||
this.state = {
|
||||
address: '',
|
||||
isLoading: false,
|
||||
@@ -50,12 +50,12 @@ class Location extends React.Component {
|
||||
const { position, record } = this.props;
|
||||
if (!isValidPosition(position?.lng, position?.lat) || typeof record !== 'object') return;
|
||||
if (prevProps.position?.lng === position?.lng && prevProps.position?.lat === position?.lat) return;
|
||||
this.currentPosition = position;
|
||||
let convertedPos = wgs84_to_gcj02(position.lng, position.lat);
|
||||
let transformedPos = wgs84_to_gcj02(position.lng, position.lat);
|
||||
if (this.mapType === MAP_TYPE.B_MAP) {
|
||||
convertedPos = gcj02_to_bd09(convertedPos.lng, convertedPos.lat);
|
||||
transformedPos = gcj02_to_bd09(transformedPos.lng, transformedPos.lat);
|
||||
}
|
||||
this.addMarkerByPosition(convertedPos.lng, convertedPos.lat);
|
||||
this.addMarkerByPosition(transformedPos.lng, transformedPos.lat);
|
||||
|
||||
this.setState({ address: record._location_translated?.address });
|
||||
}
|
||||
|
||||
@@ -68,7 +68,6 @@ class Location extends React.Component {
|
||||
|
||||
const { position, record } = this.props;
|
||||
if (!isValidPosition(position?.lng, position?.lat) || typeof record !== 'object') return;
|
||||
this.currentPosition = position;
|
||||
|
||||
this.setState({ isLoading: true, address: record._location_translated?.address });
|
||||
if (this.mapType === MAP_TYPE.B_MAP) {
|
||||
@@ -156,11 +155,15 @@ class Location extends React.Component {
|
||||
scaleControl: false,
|
||||
streetViewControl: false,
|
||||
rotateControl: false,
|
||||
fullscreenControl: false
|
||||
fullscreenControl: false,
|
||||
disableDefaultUI: true,
|
||||
gestureHandling: 'cooperative',
|
||||
});
|
||||
this.map = window.mapInstance;
|
||||
|
||||
this.addMarkerByPosition(lng, lat);
|
||||
const zoomControl = createZoomControl(this.map);
|
||||
this.map.controls[window.google.maps.ControlPosition.RIGHT_BOTTOM].push(zoomControl);
|
||||
this.map.setCenter(gcPosition);
|
||||
});
|
||||
};
|
||||
|
@@ -13,3 +13,11 @@ export const MAP_VIEW_TOOLBAR_MODE = {
|
||||
MAP: 'map',
|
||||
GALLERY: 'gallery',
|
||||
};
|
||||
|
||||
export const DEFAULT_POSITION = { lng: 104.195, lat: 35.861 };
|
||||
|
||||
export const DEFAULT_ZOOM = 4;
|
||||
|
||||
export const MAX_ZOOM = 21;
|
||||
|
||||
export const MIN_ZOOM = 3;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import { baiduMapKey, gettext } from '../../utils/constants';
|
||||
import { baiduMapKey, gettext, googleMapKey } from '../../utils/constants';
|
||||
import Icon from '../../components/icon';
|
||||
import ItemDropdownMenu from '../../components/dropdown-menu/metadata-item-dropdown-menu';
|
||||
import toaster from '../../components/toast';
|
||||
@@ -77,7 +77,7 @@ const ViewItem = ({
|
||||
const convertableViews = Object.values(VIEW_TYPE).filter(type =>
|
||||
type !== viewType &&
|
||||
type !== VIEW_TYPE.FACE_RECOGNITION &&
|
||||
!(type === VIEW_TYPE.MAP && !baiduMapKey)
|
||||
!(type === VIEW_TYPE.MAP && !baiduMapKey && !googleMapKey)
|
||||
);
|
||||
value.push({
|
||||
key: 'turn',
|
||||
|
113
frontend/src/metadata/views/map/baidu.js
Normal file
113
frontend/src/metadata/views/map/baidu.js
Normal file
@@ -0,0 +1,113 @@
|
||||
import { appAvatarURL, mediaUrl } from '../../../utils/constants';
|
||||
import { gcj02_to_bd09, wgs84_to_gcj02 } from '../../../utils/coord-transform';
|
||||
import { Utils } from '../../../utils/utils';
|
||||
import { createBMapGeolocationControl, createBMapZoomControl } from '../../components/map-controller';
|
||||
import { MIN_ZOOM, MAX_ZOOM } from '../../constants';
|
||||
import { customAvatarOverlay, customImageOverlay } from './overlay';
|
||||
|
||||
const getPoints = (images) => {
|
||||
if (!window.Cluster || !images) return [];
|
||||
return window.Cluster.pointTransformer(images, (data) => ({
|
||||
point: [data.location.lng, data.location.lat],
|
||||
properties: {
|
||||
id: data.id,
|
||||
src: data.src,
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
export const createBaiduMarkerClusterer = (map, images, onClusterLeaveIds) => {
|
||||
let clickTimeout = null;
|
||||
const cluster = new window.Cluster.View(map, {
|
||||
clusterRadius: 60,
|
||||
updateRealTime: true,
|
||||
fitViewOnClick: false,
|
||||
isAnimation: true,
|
||||
clusterMap: (properties) => ({ src: properties.src, id: properties.id }),
|
||||
clusterReduce: (acc, properties) => {
|
||||
if (!acc.properties) {
|
||||
acc.properties = [];
|
||||
}
|
||||
acc.properties.push(properties);
|
||||
},
|
||||
renderClusterStyle: {
|
||||
type: window.Cluster.ClusterRender.DOM,
|
||||
style: { offsetX: -40, offsetY: -80 },
|
||||
inject: (props) => customImageOverlay(props),
|
||||
},
|
||||
});
|
||||
|
||||
cluster.setData(getPoints(images));
|
||||
|
||||
cluster.on(window.Cluster.ClusterEvent.CLICK, (element) => {
|
||||
if (clickTimeout) {
|
||||
clearTimeout(clickTimeout);
|
||||
clickTimeout = null;
|
||||
return;
|
||||
} else {
|
||||
clickTimeout = setTimeout(() => {
|
||||
let imageIds = [];
|
||||
if (element.isCluster) {
|
||||
imageIds = cluster.getLeaves(element.id).map(item => item.properties.id).filter(Boolean);
|
||||
} else {
|
||||
imageIds = [element.properties.id];
|
||||
}
|
||||
clickTimeout = null;
|
||||
onClusterLeaveIds(imageIds);
|
||||
}, 300);
|
||||
}
|
||||
});
|
||||
|
||||
return cluster;
|
||||
};
|
||||
|
||||
export const createBaiduMap = ({ type, center, zoom, onMapState }) => {
|
||||
if (!window.BMapGL) return;
|
||||
const map = new window.BMapGL.Map('sf-metadata-map-container', {
|
||||
enableMapClick: false,
|
||||
minZoom: MIN_ZOOM,
|
||||
maxZoom: MAX_ZOOM,
|
||||
mapType: type,
|
||||
});
|
||||
|
||||
map.centerAndZoom(center, zoom);
|
||||
map.enableScrollWheelZoom(true);
|
||||
|
||||
// add controls
|
||||
const ZoomControl = createBMapZoomControl({
|
||||
anchor: window.BMAP_ANCHOR_BOTTOM_RIGHT,
|
||||
offset: new window.BMapGL.Size(66, Utils.isDesktop() ? 30 : 90),
|
||||
});
|
||||
const zoomControl = new ZoomControl();
|
||||
|
||||
const GeolocationControl = createBMapGeolocationControl({
|
||||
anchor: window.BMAP_ANCHOR_BOTTOM_RIGHT,
|
||||
offset: new window.BMapGL.Size(30, Utils.isDesktop() ? 30 : 90),
|
||||
callback: (point) => {
|
||||
const gcPosition = wgs84_to_gcj02(point.lng, point.lat);
|
||||
const bdPosition = gcj02_to_bd09(gcPosition.lng, gcPosition.lat);
|
||||
map.centerAndZoom(new window.BMapGL.Point(bdPosition.lng, bdPosition.lat), map.getZoom());
|
||||
}
|
||||
});
|
||||
const geolocationControl = new GeolocationControl();
|
||||
|
||||
map.addControl(zoomControl);
|
||||
map.addControl(geolocationControl);
|
||||
|
||||
map.addEventListener('zoomend', () => onMapState());
|
||||
|
||||
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;
|
||||
const userPosition = new window.BMapGL.Point(lng, lat);
|
||||
const imageUrl = `${mediaUrl}img/marker.png`;
|
||||
const avatarMarker = customAvatarOverlay(userPosition, appAvatarURL, imageUrl);
|
||||
map.addOverlay(avatarMarker);
|
||||
onMapState();
|
||||
});
|
||||
}
|
||||
|
||||
return map;
|
||||
};
|
103
frontend/src/metadata/views/map/google.js
Normal file
103
frontend/src/metadata/views/map/google.js
Normal file
@@ -0,0 +1,103 @@
|
||||
import { appAvatarURL, googleMapId, mediaUrl } from '../../../utils/constants';
|
||||
import { createGeolocationControl } from '../../components/map-controller/geolocation';
|
||||
import { createZoomControl } from '../../components/map-controller/zoom';
|
||||
import { MIN_ZOOM, MAX_ZOOM } from '../../constants';
|
||||
import { customImageOverlay, googleCustomAvatarOverlay } from './overlay';
|
||||
|
||||
let clickTimeout = null;
|
||||
|
||||
export const createGoogleMarkerClusterer = (map, images, onClusterLeaveIds) => {
|
||||
const markers = images.map(image => {
|
||||
const overlay = customImageOverlay({ isCluster: false, src: image.src });
|
||||
overlay.addEventListener('click', () => {
|
||||
if (clickTimeout) {
|
||||
clearTimeout(clickTimeout);
|
||||
clickTimeout = null;
|
||||
const zoom = map.getZoom();
|
||||
map.setZoom(Math.min(zoom + 1, MAX_ZOOM));
|
||||
map.setCenter(image.wgs_84);
|
||||
return;
|
||||
}
|
||||
clickTimeout = setTimeout(() => {
|
||||
onClusterLeaveIds([image.id]);
|
||||
clickTimeout = null;
|
||||
}, 300);
|
||||
});
|
||||
|
||||
return new window.google.maps.marker.AdvancedMarkerElement({
|
||||
position: image.wgs_84,
|
||||
map,
|
||||
content: overlay,
|
||||
});
|
||||
});
|
||||
|
||||
return new window.markerClusterer.MarkerClusterer({
|
||||
map,
|
||||
markers,
|
||||
renderer: {
|
||||
render: (cluster) => {
|
||||
const imagesInBounds = images.filter(image => cluster.bounds.contains(image.wgs_84));
|
||||
const overlay = customImageOverlay({ isCluster: true, reduces: { src: imagesInBounds[0].src }, pointCount: cluster.count });
|
||||
overlay.addEventListener('click', () => {
|
||||
if (clickTimeout) {
|
||||
clearTimeout(clickTimeout);
|
||||
clickTimeout = null;
|
||||
const zoom = map.getZoom();
|
||||
map.setZoom(Math.min(zoom + 1, MAX_ZOOM));
|
||||
map.setCenter(cluster.position);
|
||||
return;
|
||||
}
|
||||
clickTimeout = setTimeout(() => {
|
||||
const imagesInBounds = images.filter(image => cluster.bounds.contains(image.wgs_84));
|
||||
onClusterLeaveIds(imagesInBounds.map(image => image.id));
|
||||
clickTimeout = null;
|
||||
}, 300);
|
||||
});
|
||||
return new window.google.maps.marker.AdvancedMarkerElement({
|
||||
position: cluster.position,
|
||||
content: overlay,
|
||||
});
|
||||
}
|
||||
},
|
||||
onClusterClick: () => {},
|
||||
});
|
||||
};
|
||||
|
||||
export const createGoogleMap = ({ center, zoom, onMapState }) => {
|
||||
if (!window.google?.maps?.Map) return;
|
||||
|
||||
const map = new window.google.maps.Map(document.getElementById('sf-metadata-map-container'), {
|
||||
mapId: googleMapId,
|
||||
center,
|
||||
zoom,
|
||||
mapTypeControl: false,
|
||||
streetViewControl: false,
|
||||
fullscreenControl: false,
|
||||
cameraControl: false,
|
||||
disableDefaultUI: true,
|
||||
mapTypeId: window.google.maps.MapTypeId.ROADMAP,
|
||||
minZoom: MIN_ZOOM,
|
||||
maxZoom: MAX_ZOOM,
|
||||
});
|
||||
|
||||
const zoomControl = createZoomControl(map);
|
||||
const geolocationControl = createGeolocationControl(map);
|
||||
|
||||
map.controls[window.google.maps.ControlPosition.RIGHT_BOTTOM].push(zoomControl);
|
||||
map.controls[window.google.maps.ControlPosition.RIGHT_BOTTOM].push(geolocationControl);
|
||||
|
||||
map.addListener('center_changed', () => onMapState());
|
||||
map.addListener('zoom_changed', () => onMapState());
|
||||
|
||||
if (navigator.geolocation) {
|
||||
navigator.geolocation.getCurrentPosition((userInfo) => {
|
||||
const userPosition = { lat: userInfo.coords.latitude, lng: userInfo.coords.longitude };
|
||||
const imageUrl = `${mediaUrl}img/marker.png`;
|
||||
googleCustomAvatarOverlay(map, userPosition, appAvatarURL, imageUrl);
|
||||
onMapState();
|
||||
});
|
||||
}
|
||||
|
||||
return map;
|
||||
};
|
||||
|
@@ -5,29 +5,25 @@ import { useMetadataView } from '../../hooks/metadata-view';
|
||||
import { Utils } from '../../../utils/utils';
|
||||
import { isValidPosition } from '../../utils/validate';
|
||||
import { gcj02_to_bd09, wgs84_to_gcj02 } from '../../../utils/coord-transform';
|
||||
import loadBMap, { initMapInfo } from '../../../utils/map-utils';
|
||||
import { appAvatarURL, baiduMapKey, fileServerRoot, googleMapKey, mediaUrl, siteRoot, thumbnailSizeForGrid, thumbnailSizeForOriginal } from '../../../utils/constants';
|
||||
import { initMapInfo, loadBMap, loadGMap } from '../../../utils/map-utils';
|
||||
import { baiduMapKey, fileServerRoot, googleMapKey, siteRoot, thumbnailSizeForGrid, thumbnailSizeForOriginal } from '../../../utils/constants';
|
||||
import { MAP_TYPE as MAP_PROVIDER, PRIVATE_FILE_TYPE } from '../../../constants';
|
||||
import { EVENT_BUS_TYPE, MAP_TYPE, PREDEFINED_FILE_TYPE_OPTION_KEY, STORAGE_MAP_CENTER_KEY, STORAGE_MAP_TYPE_KEY, STORAGE_MAP_ZOOM_KEY } from '../../constants';
|
||||
import { createBMapGeolocationControl, createBMapZoomControl } from '../../components/map-controller';
|
||||
import { customAvatarOverlay, customImageOverlay } from './overlay';
|
||||
import { EVENT_BUS_TYPE, MAP_TYPE, PREDEFINED_FILE_TYPE_OPTION_KEY, STORAGE_MAP_CENTER_KEY, STORAGE_MAP_TYPE_KEY, STORAGE_MAP_ZOOM_KEY, DEFAULT_POSITION, DEFAULT_ZOOM } from '../../constants';
|
||||
import ModalPortal from '../../../components/modal-portal';
|
||||
import ImageDialog from '../../../components/dialog/image-dialog';
|
||||
import { createGoogleMap, createGoogleMarkerClusterer } from './google';
|
||||
import { createBaiduMap, createBaiduMarkerClusterer } from './baidu';
|
||||
|
||||
import './index.css';
|
||||
|
||||
const DEFAULT_POSITION = { lng: 104.195, lat: 35.861 };
|
||||
const DEFAULT_ZOOM = 4;
|
||||
const MAX_ZOOM = 21;
|
||||
const MIN_ZOOM = 3;
|
||||
|
||||
const Map = () => {
|
||||
const [imageIndex, setImageIndex] = useState(0);
|
||||
const [clusterLeaveIds, setClusterLeaveIds] = useState([]);
|
||||
const [center, setCenter] = useState(DEFAULT_POSITION);
|
||||
const [zoom, setZoom] = useState(DEFAULT_ZOOM);
|
||||
|
||||
const mapRef = useRef(null);
|
||||
const clusterRef = useRef(null);
|
||||
const clickTimeoutRef = useRef(null);
|
||||
const containerRef = useRef(null);
|
||||
const { metadata, viewID, updateCurrentPath } = useMetadataView();
|
||||
|
||||
const repoID = window.sfMetadataContext.getSetting('repoID');
|
||||
@@ -69,7 +65,8 @@ const Map = () => {
|
||||
downloadURL: `${fileServerRoot}repos/${repoID}/files${path}?op=download`,
|
||||
thumbnail,
|
||||
parentDir,
|
||||
location: bdPosition
|
||||
location: bdPosition,
|
||||
wgs_84: { lng, lat },
|
||||
};
|
||||
})
|
||||
.filter(Boolean);
|
||||
@@ -78,7 +75,19 @@ const Map = () => {
|
||||
const mapInfo = useMemo(() => initMapInfo({ baiduMapKey, googleMapKey }), []);
|
||||
const clusterLeaves = useMemo(() => images.filter(image => clusterLeaveIds.includes(image.id)), [images, clusterLeaveIds]);
|
||||
|
||||
const getPoints = useCallback(() => {
|
||||
const onMapState = 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 onClusterLeaveIds = useCallback((ids) => {
|
||||
setClusterLeaveIds(ids);
|
||||
}, []);
|
||||
|
||||
const getPoints = useCallback((images) => {
|
||||
if (!window.Cluster || !images) return [];
|
||||
return window.Cluster.pointTransformer(images, (data) => ({
|
||||
point: [data.location.lng, data.location.lat],
|
||||
@@ -87,141 +96,23 @@ const Map = () => {
|
||||
src: data.src,
|
||||
}
|
||||
}));
|
||||
}, [images]);
|
||||
|
||||
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 offset = { x: 66, y: Utils.isDesktop() ? 30 : 90 };
|
||||
const ZoomControl = createBMapZoomControl(window.BMapGL, { maxZoom: MAX_ZOOM, minZoom: MIN_ZOOM, offset }, 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 initializeCluster = useCallback(() => {
|
||||
clusterRef.current = new window.Cluster.View(mapRef.current, {
|
||||
clusterRadius: 60,
|
||||
updateRealTime: true,
|
||||
fitViewOnClick: false,
|
||||
isAnimation: true,
|
||||
clusterMap: (properties) => ({ src: properties.src, id: properties.id }),
|
||||
clusterReduce: (acc, properties) => {
|
||||
if (!acc.properties) {
|
||||
acc.properties = [];
|
||||
}
|
||||
acc.properties.push(properties);
|
||||
},
|
||||
renderClusterStyle: {
|
||||
type: window.Cluster.ClusterRender.DOM,
|
||||
style: { offsetX: -40, offsetY: -80 },
|
||||
inject: (props) => customImageOverlay(props),
|
||||
},
|
||||
});
|
||||
|
||||
clusterRef.current.setData(getPoints());
|
||||
|
||||
clusterRef.current.on(window.Cluster.ClusterEvent.CLICK, (element) => {
|
||||
if (clickTimeoutRef.current) {
|
||||
clearTimeout(clickTimeoutRef.current);
|
||||
clickTimeoutRef.current = null;
|
||||
return;
|
||||
} else {
|
||||
clickTimeoutRef.current = setTimeout(() => {
|
||||
let imageIds = [];
|
||||
if (element.isCluster) {
|
||||
imageIds = clusterRef.current.getLeaves(element.id).map(item => item.properties.id).filter(Boolean);
|
||||
} else {
|
||||
imageIds = [element.properties.id];
|
||||
}
|
||||
clickTimeoutRef.current = null;
|
||||
setClusterLeaveIds(imageIds);
|
||||
}, 300);
|
||||
}
|
||||
});
|
||||
window.BMapCluster = clusterRef.current;
|
||||
}, [getPoints]);
|
||||
|
||||
const renderBaiduMap = useCallback(() => {
|
||||
if (!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 = createBaiduMap({ images, center, zoom, getPoints, onMapState, onClusterLeaveIds });
|
||||
createBaiduMarkerClusterer(mapRef.current, images, onClusterLeaveIds);
|
||||
|
||||
if (!window.BMapInstance) {
|
||||
mapRef.current = new window.BMapGL.Map('sf-metadata-map-container', {
|
||||
enableMapClick: false,
|
||||
minZoom: MIN_ZOOM,
|
||||
maxZoom: MAX_ZOOM,
|
||||
mapType: getBMapType(mapTypeValue),
|
||||
});
|
||||
window.BMapInstance = mapRef.current;
|
||||
window.mapViewInstance = mapRef.current;
|
||||
}, [images, center, zoom, getPoints, onMapState, onClusterLeaveIds]);
|
||||
|
||||
if (isValidPosition(center?.lng, center?.lat)) {
|
||||
mapRef.current.centerAndZoom(center, zoom);
|
||||
}
|
||||
const renderGoogleMap = useCallback(() => {
|
||||
if (!window.google?.maps?.Map) return;
|
||||
mapRef.current = createGoogleMap({ center, zoom, onMapState });
|
||||
createGoogleMarkerClusterer(mapRef.current, images, onClusterLeaveIds);
|
||||
|
||||
mapRef.current.enableScrollWheelZoom(true);
|
||||
addMapController();
|
||||
|
||||
initializeUserMarker(userPosition);
|
||||
initializeCluster();
|
||||
} else {
|
||||
const viewDom = document.getElementById('sf-metadata-view-map');
|
||||
const container = window.BMapInstance.getContainer();
|
||||
viewDom.replaceChild(container, mapRef.current);
|
||||
|
||||
mapRef.current = window.BMapInstance;
|
||||
clusterRef.current = window.BMapCluster;
|
||||
clusterRef.current.setData(getPoints());
|
||||
}
|
||||
}, [addMapController, initializeCluster, initializeUserMarker, getBMapType, loadMapState, getPoints]);
|
||||
window.mapViewInstance = mapRef.current;
|
||||
}, [images, center, zoom, onMapState, onClusterLeaveIds]);
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
setImageIndex(0);
|
||||
@@ -236,21 +127,60 @@ const Map = () => {
|
||||
setImageIndex((imageIndex + 1) % clusterLeaves.length);
|
||||
}, [imageIndex, clusterLeaves.length]);
|
||||
|
||||
const onMapTypeChange = useCallback((newType) => {
|
||||
window.sfMetadataContext.localStorage.setItem(STORAGE_MAP_TYPE_KEY, newType);
|
||||
|
||||
if (mapInfo.type === MAP_PROVIDER.B_MAP) {
|
||||
const baiduMapType = {
|
||||
[MAP_TYPE.MAP]: window.BMAP_NORMAL_MAP,
|
||||
[MAP_TYPE.SATELLITE]: window.BMAP_SATELLITE_MAP
|
||||
}[newType] || window.BMAP_NORMAL_MAP;
|
||||
|
||||
mapRef.current?.setMapType(baiduMapType);
|
||||
} else if (mapInfo.type === MAP_PROVIDER.G_MAP) {
|
||||
const googleMapType = {
|
||||
[MAP_TYPE.MAP]: window.google.maps.MapTypeId.ROADMAP,
|
||||
[MAP_TYPE.SATELLITE]: window.google.maps.MapTypeId.HYBRID
|
||||
}[newType] || window.google.maps.MapTypeId.ROADMAP;
|
||||
|
||||
mapRef.current?.setMapTypeId(googleMapType);
|
||||
}
|
||||
|
||||
mapRef.current?.setCenter(mapRef.current.getCenter());
|
||||
}, [mapInfo.type]);
|
||||
|
||||
const onClearMapInstance = useCallback(() => {
|
||||
if (window.mapViewInstance) {
|
||||
if (mapInfo.type === MAP_PROVIDER.B_MAP) {
|
||||
window.mapViewInstance.destroy();
|
||||
} else if (mapInfo.type === MAP_PROVIDER.G_MAP) {
|
||||
window.mapViewInstance.setMap(null);
|
||||
}
|
||||
delete window.mapViewInstance;
|
||||
}
|
||||
mapRef.current = null;
|
||||
}, [mapInfo.type]);
|
||||
|
||||
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;
|
||||
setCenter(savedCenter);
|
||||
setZoom(savedZoom);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
updateCurrentPath(`/${PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES}/${viewID}`);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
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);
|
||||
mapRef.current.setCenter(mapRef.current.getCenter());
|
||||
});
|
||||
loadMapState();
|
||||
const unsubscribeModifyMapType = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_MAP_TYPE, onMapTypeChange);
|
||||
const unsubscribeClearMapInstance = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.CLEAR_MAP_INSTANCE, onClearMapInstance);
|
||||
|
||||
return () => {
|
||||
modifyMapTypeSubscribe();
|
||||
unsubscribeModifyMapType();
|
||||
unsubscribeClearMapInstance();
|
||||
};
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@@ -258,18 +188,34 @@ const Map = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (mapInfo.type === MAP_PROVIDER.B_MAP) {
|
||||
loadBMap(mapInfo.key).then(() => renderBaiduMap());
|
||||
return () => {
|
||||
window.renderMap = null;
|
||||
};
|
||||
if (!window.mapViewInstance) {
|
||||
loadBMap(mapInfo.key).then(() => renderBaiduMap());
|
||||
} else {
|
||||
const viewDom = document.getElementById('sf-metadata-view-map');
|
||||
const container = window.mapViewInstance.getContainer();
|
||||
viewDom.replaceChild(container, containerRef.current);
|
||||
|
||||
mapRef.current = window.mapViewInstance;
|
||||
createBaiduMarkerClusterer(mapRef.current, images, onClusterLeaveIds);
|
||||
}
|
||||
} else if (mapInfo.type === MAP_PROVIDER.G_MAP) {
|
||||
if (!window.mapViewInstance) {
|
||||
loadGMap(mapInfo.key).then(() => renderGoogleMap());
|
||||
} else {
|
||||
const viewDom = document.getElementById('sf-metadata-view-map');
|
||||
const container = window.mapViewInstance.getDiv();
|
||||
viewDom.replaceChild(container, containerRef.current);
|
||||
|
||||
mapRef.current = window.mapViewInstance;
|
||||
createGoogleMarkerClusterer(mapRef.current, images, onClusterLeaveIds);
|
||||
}
|
||||
}
|
||||
return;
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="sf-metadata-view-map" id="sf-metadata-view-map">
|
||||
<div ref={mapRef} className="sf-metadata-map-container" id="sf-metadata-map-container"></div>
|
||||
<div ref={containerRef} className="sf-metadata-map-container" id="sf-metadata-map-container"></div>
|
||||
{clusterLeaveIds.length > 0 && (
|
||||
<ModalPortal>
|
||||
<ImageDialog
|
||||
|
@@ -1,4 +1,4 @@
|
||||
const customAvatarOverlay = (point, avatarUrl, bgUrl, width = 20, height = 25) => {
|
||||
export const customAvatarOverlay = (point, avatarUrl, bgUrl, width = 20, height = 25) => {
|
||||
class AvatarOverlay extends window.BMapGL.Overlay {
|
||||
constructor(point, avatarUrl, bgUrl, width, height) {
|
||||
super();
|
||||
@@ -44,4 +44,70 @@ const customAvatarOverlay = (point, avatarUrl, bgUrl, width = 20, height = 25) =
|
||||
return new AvatarOverlay(point, avatarUrl, bgUrl, width, height);
|
||||
};
|
||||
|
||||
export default customAvatarOverlay;
|
||||
export const googleCustomAvatarOverlay = (map, position, avatarUrl, bgUrl, width = 20, height = 25) => {
|
||||
class AvatarOverlay extends window.google.maps.OverlayView {
|
||||
constructor(map, position, avatarUrl, bgUrl, width, height) {
|
||||
super();
|
||||
this._position = position;
|
||||
this._avatarUrl = avatarUrl;
|
||||
this._bgUrl = bgUrl;
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
this._div = null;
|
||||
this.setMap(map);
|
||||
}
|
||||
|
||||
onAdd() {
|
||||
this.createDomElements();
|
||||
const panes = this.getPanes();
|
||||
panes.overlayLayer.appendChild(this._div);
|
||||
}
|
||||
|
||||
draw() {
|
||||
const overlayProjection = this.getProjection();
|
||||
const pos = overlayProjection.fromLatLngToDivPixel(this._position);
|
||||
|
||||
this._div.style.left = `${pos.x - this._width / 2}px`;
|
||||
this._div.style.top = `${pos.y - (this._height * 7) / 10}px`;
|
||||
}
|
||||
|
||||
onRemove() {
|
||||
this._div.parentNode?.removeChild(this._div);
|
||||
this._div = null;
|
||||
}
|
||||
|
||||
createDomElements() {
|
||||
this._div = document.createElement('div');
|
||||
this._div.className = 'custom-avatar-overlay';
|
||||
Object.assign(this._div.style, {
|
||||
position: 'absolute',
|
||||
width: `${this._width}px`,
|
||||
height: `${this._height}px`,
|
||||
backgroundImage: `url(${this._bgUrl})`,
|
||||
backgroundPosition: '.5px 0px',
|
||||
display: 'flex',
|
||||
padding: '2px 2.5px 0 2px'
|
||||
});
|
||||
|
||||
const img = document.createElement('img');
|
||||
img.src = this._avatarUrl;
|
||||
Object.assign(img.style, {
|
||||
width: '16px',
|
||||
height: '16px',
|
||||
borderRadius: '50%',
|
||||
display: 'block'
|
||||
});
|
||||
|
||||
this._div.appendChild(img);
|
||||
}
|
||||
}
|
||||
|
||||
return new AvatarOverlay(
|
||||
map,
|
||||
position,
|
||||
avatarUrl,
|
||||
bgUrl,
|
||||
width,
|
||||
height
|
||||
);
|
||||
};
|
||||
|
@@ -1,7 +1,8 @@
|
||||
import customAvatarOverlay from './custom-avatar-overlay';
|
||||
import { customAvatarOverlay, googleCustomAvatarOverlay } from './custom-avatar-overlay';
|
||||
import customImageOverlay from './custom-image-overlay';
|
||||
|
||||
export {
|
||||
customAvatarOverlay,
|
||||
googleCustomAvatarOverlay,
|
||||
customImageOverlay
|
||||
};
|
||||
|
@@ -8,26 +8,27 @@ export const initMapInfo = ({ baiduMapKey, googleMapKey, mineMapKey }) => {
|
||||
};
|
||||
|
||||
export const loadMapSource = (type, key, callback) => {
|
||||
if (!type || !key) return;
|
||||
let scriptUrl = '';
|
||||
const sourceId = 'map-source-script';
|
||||
|
||||
if (document.getElementById(sourceId)) return;
|
||||
let script = document.createElement('script');
|
||||
script.type = 'text/javascript';
|
||||
script.id = sourceId;
|
||||
|
||||
if (type === MAP_TYPE.B_MAP) {
|
||||
scriptUrl = `https://api.map.baidu.com/api?type=webgl&v=3.0&ak=${key}&callback=renderBaiduMap`;
|
||||
} else if (type === MAP_TYPE.G_MAP) {
|
||||
scriptUrl = `https://maps.googleapis.com/maps/api/js?key=${key}&callback=renderGoogleMap&libraries=marker&v=weekly`;
|
||||
scriptUrl = `https://maps.googleapis.com/maps/api/js?key=${key}&libraries=marker,geometry&v=weekly&callback=renderGoogleMap`;
|
||||
}
|
||||
|
||||
if (scriptUrl) {
|
||||
const script = document.createElement('script');
|
||||
script.id = sourceId;
|
||||
script.src = scriptUrl;
|
||||
script.onload = callback;
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
callback && callback();
|
||||
};
|
||||
|
||||
export default function loadBMap(ak) {
|
||||
export function loadBMap(ak) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (typeof window.BMapGL !== 'undefined' && document.querySelector(`script[src*="${mediaUrl}js/map/cluster.js"]`)) {
|
||||
resolve(true);
|
||||
@@ -57,6 +58,36 @@ export function asyncLoadBaiduJs(ak) {
|
||||
});
|
||||
}
|
||||
|
||||
export function loadGMap(ak) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (typeof window.google !== 'undefined' && document.querySelector(`script[src*="${mediaUrl}js/map/cluster.js"]`)) {
|
||||
resolve(true);
|
||||
return;
|
||||
}
|
||||
asyncLoadGMapJs(ak)
|
||||
.then(() => asyncLoadJs('https://unpkg.com/@googlemaps/markerclusterer/dist/index.min.js'))
|
||||
.then(() => resolve(true))
|
||||
.catch((err) => reject(err));
|
||||
});
|
||||
}
|
||||
|
||||
export function asyncLoadGMapJs(key) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (typeof window.google !== 'undefined') {
|
||||
resolve(window.google);
|
||||
return;
|
||||
}
|
||||
window.renderGoogleMap = function () {
|
||||
resolve(window.google);
|
||||
};
|
||||
let script = document.createElement('script');
|
||||
script.type = 'text/javascript';
|
||||
script.src = `https://maps.googleapis.com/maps/api/js?key=${key}&libraries=marker,geometry&v=weekly&callback=renderGoogleMap&loading=async`;
|
||||
script.onerror = reject;
|
||||
document.body.appendChild(script);
|
||||
});
|
||||
}
|
||||
|
||||
export function asyncLoadJs(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let script = document.createElement('script');
|
||||
|
Reference in New Issue
Block a user