diff --git a/frontend/src/metadata/views/map/index.js b/frontend/src/metadata/views/map/index.js
index df78a5a991..b6d25eecce 100644
--- a/frontend/src/metadata/views/map/index.js
+++ b/frontend/src/metadata/views/map/index.js
@@ -1,11 +1,10 @@
-import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
+import React, { useEffect, useMemo } from 'react';
import { getFileNameFromRecord, getFileTypeFromRecord, getImageLocationFromRecord, getParentDirFromRecord, getRecordIdFromRecord } from '../../utils/cell';
-import ClusterPhotos from './cluster-photos';
import MapView from './map-view';
import { PREDEFINED_FILE_TYPE_OPTION_KEY } from '../../constants';
import { useMetadataView } from '../../hooks/metadata-view';
import { Utils } from '../../../utils/utils';
-import { gettext, siteRoot, thumbnailSizeForGrid } from '../../../utils/constants';
+import { fileServerRoot, siteRoot, thumbnailSizeForGrid, thumbnailSizeForOriginal } from '../../../utils/constants';
import { isValidPosition } from '../../utils/validate';
import { gcj02_to_bd09, wgs84_to_gcj02 } from '../../../utils/coord-transform';
import { PRIVATE_FILE_TYPE } from '../../../constants';
@@ -13,12 +12,10 @@ import { PRIVATE_FILE_TYPE } from '../../../constants';
import './index.css';
const Map = () => {
- const [showCluster, setShowCluster] = useState(false);
const { metadata, viewID, updateCurrentPath } = useMetadataView();
- const clusterRef = useRef([]);
-
const repoID = window.sfMetadataContext.getSetting('repoID');
+ const repoInfo = window.sfMetadataContext.getSetting('repoInfo');
const images = useMemo(() => {
return metadata.rows
@@ -26,44 +23,47 @@ const Map = () => {
const recordType = getFileTypeFromRecord(record);
if (recordType !== PREDEFINED_FILE_TYPE_OPTION_KEY.PICTURE) return null;
const id = getRecordIdFromRecord(record);
- const fileName = getFileNameFromRecord(record);
+ const name = getFileNameFromRecord(record);
const parentDir = getParentDirFromRecord(record);
- const path = Utils.encodePath(Utils.joinPath(parentDir, fileName));
- const src = `${siteRoot}thumbnail/${repoID}/${thumbnailSizeForGrid}${path}`;
+ const path = Utils.encodePath(Utils.joinPath(parentDir, name));
const location = getImageLocationFromRecord(record);
if (!location) return null;
const { lng, lat } = location;
if (!isValidPosition(lng, lat)) return null;
const gcPosition = wgs84_to_gcj02(lng, lat);
const bdPosition = gcj02_to_bd09(gcPosition.lng, gcPosition.lat);
- return { id, src, lng: bdPosition.lng, lat: bdPosition.lat };
+
+ const repoEncrypted = repoInfo.encrypted;
+ const cacheBuster = new Date().getTime();
+ const fileExt = name.substr(name.lastIndexOf('.') + 1).toLowerCase();
+ let thumbnail = '';
+ const isGIF = fileExt === 'gif';
+ if (repoEncrypted || isGIF) {
+ thumbnail = `${siteRoot}repo/${repoID}/raw${path}?t=${cacheBuster}`;
+ } else {
+ thumbnail = `${siteRoot}thumbnail/${repoID}/${thumbnailSizeForOriginal}${path}`;
+ }
+
+ return {
+ id,
+ name,
+ src: `${siteRoot}thumbnail/${repoID}/${thumbnailSizeForGrid}${path}`,
+ url: `${siteRoot}lib/${repoID}/file${path}`,
+ downloadURL: `${fileServerRoot}repos/${repoID}/files${path}?op=download`,
+ thumbnail,
+ parentDir,
+ location: { lng: bdPosition.lng, lat: bdPosition.lat }
+ };
})
.filter(Boolean);
- }, [repoID, metadata.rows]);
-
- const openCluster = useCallback((clusterIds) => {
- clusterRef.current = clusterIds;
- updateCurrentPath(`/${PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES}/${viewID}/${gettext('Location')}`);
- setShowCluster(true);
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [viewID, updateCurrentPath]);
-
- const closeCluster = useCallback(() => {
- clusterRef.current = [];
- updateCurrentPath(`/${PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES}/${viewID}`);
- setShowCluster(false);
- }, [viewID, updateCurrentPath]);
+ }, [repoID, repoInfo.encrypted, metadata]);
useEffect(() => {
updateCurrentPath(`/${PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES}/${viewID}`);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
- if (showCluster) {
- return ();
- }
-
- return ();
+ return ();
};
export default Map;
diff --git a/frontend/src/metadata/views/map/map-view/index.js b/frontend/src/metadata/views/map/map-view/index.js
index 9b07c82a03..dea03ed4b7 100644
--- a/frontend/src/metadata/views/map/map-view/index.js
+++ b/frontend/src/metadata/views/map/map-view/index.js
@@ -1,4 +1,4 @@
-import React, { useCallback, useEffect, useMemo, useRef } from 'react';
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import loadBMap, { initMapInfo } from '../../../../utils/map-utils';
import { appAvatarURL, baiduMapKey, googleMapKey, mediaUrl } from '../../../../utils/constants';
@@ -8,21 +8,27 @@ 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 ModalPortal from '../../../../components/modal-portal';
+import ImageDialog from '../../../../components/dialog/image-dialog';
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 MapView = ({ images }) => {
+ const [imageIndex, setImageIndex] = useState(0);
+ const [clusterLeaveIds, setClusterLeaveIds] = useState([]);
+
const mapInfo = useMemo(() => initMapInfo({ baiduMapKey, googleMapKey }), []);
+ const clusterLeaves = useMemo(() => images.filter(image => clusterLeaveIds.includes(image.id)), [images, clusterLeaveIds]);
const mapRef = useRef(null);
const clusterRef = useRef(null);
const batchIndexRef = useRef(0);
+ const clickTimeoutRef = useRef(null);
const saveMapState = useCallback(() => {
if (!mapRef.current) return;
@@ -68,44 +74,57 @@ const MapView = ({ images, onOpenCluster }) => {
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 getPoints = useCallback((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,
+ }
+ }));
+ }, []);
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]);
+ clusterRef.current = new window.Cluster.View(mapRef.current, {
+ clusterRadius: 80,
+ 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,
+ inject: (props) => customImageOverlay(props),
+ },
+ });
+
+ clusterRef.current.setData(getPoints(images));
+
+ 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);
+ }
+ });
+ }, [images, getPoints]);
const renderBaiduMap = useCallback(() => {
if (!mapRef.current || !window.BMapGL.Map) return;
@@ -141,8 +160,20 @@ const MapView = ({ images, onOpenCluster }) => {
initializeCluster();
batchIndexRef.current = 0;
- renderMarkersBatch();
- }, [addMapController, initializeCluster, initializeUserMarker, renderMarkersBatch, getBMapType, loadMapState]);
+ }, [addMapController, initializeCluster, initializeUserMarker, getBMapType, loadMapState]);
+
+ const handleClose = useCallback(() => {
+ setImageIndex(0);
+ setClusterLeaveIds([]);
+ }, []);
+
+ const moveToPrevImage = useCallback(() => {
+ setImageIndex((imageIndex + clusterLeaves.length - 1) % clusterLeaves.length);
+ }, [imageIndex, clusterLeaves.length]);
+
+ const moveToNextImage = useCallback(() => {
+ setImageIndex((imageIndex + 1) % clusterLeaves.length);
+ }, [imageIndex, clusterLeaves.length]);
useEffect(() => {
const modifyMapTypeSubscribe = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_MAP_TYPE, (newType) => {
@@ -172,6 +203,17 @@ const MapView = ({ images, onOpenCluster }) => {
return (
+ {clusterLeaveIds.length > 0 && (
+
+
+
+ )}
);
};
diff --git a/frontend/src/metadata/views/map/map-view/overlay/custom-image-overlay.js b/frontend/src/metadata/views/map/map-view/overlay/custom-image-overlay.js
index 871e004617..60e5e77c3c 100644
--- a/frontend/src/metadata/views/map/map-view/overlay/custom-image-overlay.js
+++ b/frontend/src/metadata/views/map/map-view/overlay/custom-image-overlay.js
@@ -1,86 +1,36 @@
-import { Utils } from '../../../../../utils/utils';
+const OVERLAY_SIZE = 80;
-const customImageOverlay = (center, image, callback) => {
- class ImageOverlay extends window.BMapLib.TextIconOverlay {
- constructor(center, image, { callback } = {}) {
- super(center, '', { styles: [] });
- this._center = center;
- this._URL = image.src;
- this._id = image.id;
- this._callback = callback;
- }
+const customImageOverlay = (props) => {
+ const { isCluster, pointCount, reduces } = props;
+ const src = isCluster ? reduces.src : props.src;
- initialize(map) {
- this._map = map;
- const div = document.createElement('div');
- div.style.position = 'absolute';
- div.style.zIndex = 2000;
- map.getPanes().markerPane.appendChild(div);
- this._div = div;
+ const div = document.createElement('div');
+ div.style.position = 'absolute';
- const imageElement = `
`;
- const htmlString =
- `
-
- ${this._URL ? imageElement : '
'}
-
- `;
- const labelDocument = new DOMParser().parseFromString(htmlString, 'text/html');
- const label = labelDocument.body.firstElementChild;
- this._div.append(label);
+ const container = document.createElement('div');
+ container.className = 'custom-image-container';
- const eventHandler = (event) => {
- event.preventDefault();
- this._callback && this._callback(event, [{ _id: this._id }]);
- };
-
- if (Utils.isDesktop()) {
- let clickTimeout;
- this._div.addEventListener('click', (event) => {
- if (clickTimeout) {
- clearTimeout(clickTimeout);
- clickTimeout = null;
- return;
- }
- clickTimeout = setTimeout(() => {
- eventHandler(event);
- clickTimeout = null;
- }, 300);
- });
- this._div.addEventListener('dblclick', (e) => {
- e.preventDefault();
- if (clickTimeout) {
- clearTimeout(clickTimeout);
- clickTimeout = null;
- }
- });
- } else {
- this._div.addEventListener('touchend', eventHandler);
- }
-
- return div;
- }
-
- draw() {
- const position = this._map.pointToOverlayPixel(this._center);
- this._div.style.left = position.x - 40 + 'px'; // 40 is 1/2 container height
- this._div.style.top = position.y - 88 + 'px'; // 80 is container height and 8 is icon height
- }
-
- getImageUrl() {
- return image.src || '';
- }
-
- getPosition() {
- return center;
- }
-
- getMap() {
- return this._map || null;
- }
+ if (isCluster && pointCount > 1) {
+ const customImageNumber = document.createElement('span');
+ customImageNumber.className = 'custom-image-number';
+ customImageNumber.innerText = pointCount < 1000 ? pointCount : '1k+';
+ container.appendChild(customImageNumber);
}
- return new ImageOverlay(center, image, callback);
+ if (src) {
+ const imageElement = document.createElement('img');
+ imageElement.src = src;
+ imageElement.width = OVERLAY_SIZE;
+ imageElement.height = OVERLAY_SIZE;
+ container.appendChild(imageElement);
+ } else {
+ const emptyImageWrapper = document.createElement('div');
+ emptyImageWrapper.className = 'empty-custom-image-wrapper';
+ container.appendChild(emptyImageWrapper);
+ }
+
+ div.appendChild(container);
+ return div;
};
export default customImageOverlay;
diff --git a/frontend/src/utils/map-utils.js b/frontend/src/utils/map-utils.js
index dfa0982225..7fc88af689 100644
--- a/frontend/src/utils/map-utils.js
+++ b/frontend/src/utils/map-utils.js
@@ -31,9 +31,12 @@ export const loadMapSource = (type, key, callback) => {
export default function loadBMap(ak) {
return new Promise((resolve, reject) => {
+ if (typeof window.BMapGL !== 'undefined' && document.querySelector(`script[src*="${mediaUrl}js/map/cluster.js"]`)) {
+ resolve(true);
+ return;
+ }
asyncLoadBaiduJs(ak)
- .then(() => asyncLoadJs(`${mediaUrl}/js/map/text-icon-overlay.js?v=${STATIC_RESOURCE_VERSION}`))
- .then(() => asyncLoadJs(`${mediaUrl}/js/map/marker-cluster.js?v=${STATIC_RESOURCE_VERSION}`))
+ .then(() => asyncLoadJs(`${mediaUrl}js/map/cluster.js`))
.then(() => resolve(true))
.catch((err) => reject(err));
});
diff --git a/media/js/map/cluster.js b/media/js/map/cluster.js
new file mode 100644
index 0000000000..90da9ede1b
--- /dev/null
+++ b/media/js/map/cluster.js
@@ -0,0 +1 @@
+!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).Cluster=e()}(this,(function(){"use strict";class t{constructor(e){this._hashCode=null!=e?e:t.guid()}getHandlers(t){let e=this._events||(this._events=new Map);return e.has(t)||e.set(t,new Map),e.get(t)}on(e,s){if("function"!=typeof s)return;this.getHandlers(e).set(s,t.guid())}addEventListener(t,e){this.on(t,e)}off(t,e){var s;let i=this.getHandlers(t);e?i.has(e)&&i.delete(e):null===(s=this._events)||void 0===s||s.clear()}removeEventListener(t,e){this.off(t,e)}fire(t,e){let s=Array.from(this.getHandlers(t).entries());for(const[t]of s)t.call(this,e)}dispatchEvent(t,e){this.fire(t,e)}destroy(){var t;null===(t=this._events)||void 0===t||t.clear(),this._events=null}get hashCode(){return this._hashCode}static guid(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(function(t){let e=16*Math.random()|0;return("x"==t?e:3&e|8).toString(16)}))}}var e,s,i,r;!function(t){t.CLICK="click",t.MOUSE_OVER="mouseover",t.MOUSE_OUT="mouseout",t.CHANGE="change",t.DESTROY="destroy"}(e||(e={})),function(t){t.DIS_PIXEL="dis-pixel",t.ATTR_REF="attribute",t.GEO_FENCE="geo-fence"}(s||(s={})),function(t){t.DOM="dom",t.WEBGL="webgl"}(i||(i={})),function(t){t.MULTI="multi",t.SINGLE="single"}(r||(r={}));const n=[Int8Array,Uint8Array,Uint8ClampedArray,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];class o{static from(t){if(!(t instanceof ArrayBuffer))throw new Error("Data must be an instance of ArrayBuffer.");const[e,s]=new Uint8Array(t,0,2);if(219!==e)throw new Error("Data does not appear to be in a KDBush format.");const i=s>>4;if(1!==i)throw new Error(`Got v${i} data when expected v1.`);const r=n[15&s];if(!r)throw new Error("Unrecognized array type.");const[l]=new Uint16Array(t,2,1),[h]=new Uint32Array(t,4,1);return new o(h,l,r,t)}constructor(t,e=64,s=Float64Array,i){if(isNaN(t)||t<0)throw new Error(`Unpexpected numItems value: ${t}.`);this.numItems=+t,this.nodeSize=Math.min(Math.max(+e,2),65535),this.ArrayType=s,this.IndexArrayType=t<65536?Uint16Array:Uint32Array;const r=n.indexOf(this.ArrayType),o=2*t*this.ArrayType.BYTES_PER_ELEMENT,l=t*this.IndexArrayType.BYTES_PER_ELEMENT,h=(8-l%8)%8;if(r<0)throw new Error(`Unexpected typed array class: ${s}.`);i&&i instanceof ArrayBuffer?(this.data=i,this.ids=new this.IndexArrayType(this.data,8,t),this.coords=new this.ArrayType(this.data,8+l+h,2*t),this._pos=2*t,this._finished=!0):(this.data=new ArrayBuffer(8+o+l+h),this.ids=new this.IndexArrayType(this.data,8,t),this.coords=new this.ArrayType(this.data,8+l+h,2*t),this._pos=0,this._finished=!1,new Uint8Array(this.data,0,2).set([219,16+r]),new Uint16Array(this.data,2,1)[0]=e,new Uint32Array(this.data,4,1)[0]=t)}add(t,e){const s=this._pos>>1;return this.ids[s]=s,this.coords[this._pos++]=t,this.coords[this._pos++]=e,s}finish(){const t=this._pos>>1;if(t!==this.numItems)throw new Error(`Added ${t} items when expected ${this.numItems}.`);return l(this.ids,this.coords,this.nodeSize,0,this.numItems-1,0),this._finished=!0,this}range(t,e,s,i){if(!this._finished)throw new Error("Data not yet indexed - call index.finish().");const{ids:r,coords:n,nodeSize:o}=this,l=[0,r.length-1,0],h=[];for(;l.length;){const a=l.pop()||0,u=l.pop()||0,p=l.pop()||0;if(u-p<=o){for(let o=p;o<=u;o++){const l=n[2*o],a=n[2*o+1];l>=t&&l<=s&&a>=e&&a<=i&&h.push(r[o])}continue}const c=p+u>>1,g=n[2*c],d=n[2*c+1];g>=t&&g<=s&&d>=e&&d<=i&&h.push(r[c]),(0===a?t<=g:e<=d)&&(l.push(p),l.push(c-1),l.push(1-a)),(0===a?s>=g:i>=d)&&(l.push(c+1),l.push(u),l.push(1-a))}return h}within(t,e,s){if(!this._finished)throw new Error("Data not yet indexed - call index.finish().");const{ids:i,coords:r,nodeSize:n}=this,o=[0,i.length-1,0],l=[],h=s*s;for(;o.length;){const a=o.pop()||0,u=o.pop()||0,c=o.pop()||0;if(u-c<=n){for(let s=c;s<=u;s++)p(r[2*s],r[2*s+1],t,e)<=h&&l.push(i[s]);continue}const g=c+u>>1,d=r[2*g],y=r[2*g+1];p(d,y,t,e)<=h&&l.push(i[g]),(0===a?t-s<=d:e-s<=y)&&(o.push(c),o.push(g-1),o.push(1-a)),(0===a?t+s>=d:e+s>=y)&&(o.push(g+1),o.push(u),o.push(1-a))}return l}}function l(t,e,s,i,r,n){if(r-i<=s)return;const o=i+r>>1;h(t,e,o,i,r,n),l(t,e,s,i,o-1,1-n),l(t,e,s,o+1,r,1-n)}function h(t,e,s,i,r,n){for(;r>i;){if(r-i>600){const o=r-i+1,l=s-i+1,a=Math.log(o),u=.5*Math.exp(2*a/3),p=.5*Math.sqrt(a*u*(o-u)/o)*(l-o/2<0?-1:1);h(t,e,s,Math.max(i,Math.floor(s-l*u/o+p)),Math.min(r,Math.floor(s+(o-l)*u/o+p)),n)}const o=e[2*s+n];let l=i,u=r;for(a(t,e,i,s),e[2*r+n]>o&&a(t,e,i,r);lo;)u--}e[2*i+n]===o?a(t,e,i,u):(u++,a(t,e,u,r)),u<=s&&(i=u+1),s<=u&&(r=u-1)}}function a(t,e,s,i){u(t,s,i),u(e,2*s,2*i),u(e,2*s+1,2*i+1)}function u(t,e,s){const i=t[e];t[e]=t[s],t[s]=i}function p(t,e,s,i){const r=t-s,n=e-i;return r*r+n*n}function c(t){return!isNaN(t)&&null!==t&&!Array.isArray(t)}function g(t,e,s){if(null!==t)for(var i,r,n,o,l,h,a,u,p=0,c=0,d=t.type,y="FeatureCollection"===d,m="Feature"===d,f=y?t.features.length:1,_=0;_t[0]&&(e[0]=t[0]),e[1]>t[1]&&(e[1]=t[1]),e[2]=2&&!Array.isArray(t[0])&&!Array.isArray(t[1]))return t;throw new Error("coord must be GeoJSON Point or an Array of numbers")}(t),n="Feature"===(i=e).type?i.geometry:i,o=n.type,l=e.bbox,h=n.coordinates;if(l&&!1===function(t,e){return e[0]<=t[0]&&e[1]<=t[1]&&e[2]>=t[0]&&e[3]>=t[1]}(r,l))return!1;"Polygon"===o&&(h=[h]);for(var a=!1,u=0;ut[1]!=a>t[1]&&t[0]<(h-o)*(t[1]-l)/(a-l)+o&&(i=!i)}return i}function f(t,e){void 0===e&&(e={});var s=0,i=0,r=0;return g(t,(function(t){s+=t[0],i+=t[1],r++}),!0),function(t,e,s){if(void 0===s&&(s={}),!t)throw new Error("coordinates is required");if(!Array.isArray(t))throw new Error("coordinates must be an Array");if(t.length<2)throw new Error("coordinates must be at least 2 numbers long");if(!c(t[0])||!c(t[1]))throw new Error("coordinates must contain numbers");return function(t,e,s){void 0===s&&(s={});var i={type:"Feature"};return(0===s.id||s.id)&&(i.id=s.id),s.bbox&&(i.bbox=s.bbox),i.properties=e||{},i.geometry=t,i}({type:"Point",coordinates:t},e,s)}([s/r,i/r],e.properties)}d.default=d;const _=(t,e,s)=>{let i,r,n,o=null,l=0;s||(s={});let h=function(){l=!1===s.leading?0:Date.now(),o=null,n=t.apply(r,i),o||(r=i=null)};return function(){let a=Date.now();l||!1!==s.leading||(l=a);let u=e-(a-l);return r=this,i=arguments,u<=0||u>e?(o&&(clearTimeout(o),o=null),l=a,n=t.apply(r,i),o||(r=i=null)):o||!1===s.trailing||(o=setTimeout(h,u)),n}},w=Math.fround||(E=new Float32Array(1),t=>(E[0]=+t,E[0]));var E;const O=(t,e,s)=>t.reduce(((t,i,r)=>{let n=e(i);return n=null==n||""===n?"undefined":n,t[n]||(t[n]=[]),s.type&&s.zoom&&(i["_inner_"+s.type+"_"+s.zoom]=n),i.id=r,t[n].push(i),t}),{}),T=t=>({fence:!0,bbox:d(t),center:v(t)}),M=(t,e)=>{if(e){return d({type:"FeatureCollection",features:[{type:"Feature",properties:{},geometry:{type:"Polygon",coordinates:[t]}}]})}return d({type:"FeatureCollection",features:t})},v=t=>f(t).geometry.coordinates,L=t=>{if(!t||!Array.isArray(t)||t.length<2)return[];let e=[t[0],t[1]];for(let s=2;st/360+.5,C=t=>{const e=Math.sin(t*Math.PI/180),s=.5-.25*Math.log((1+e)/(1-e))/Math.PI;return s<0?0:s>1?1:s};class x{constructor(){}}var S;!function(t){t[t.NONE=0]="NONE",t[t.INNER=1]="INNER",t[t.OUT=2]="OUT",t[t.ALL=3]="ALL"}(S||(S={}));class b{constructor(t,s=!1){this.trees={},this.nodeSize=64,this.isSort=!0,this.splitChar="*.*",this.isWeight=!1,this.OFFSET_ZOOM=2,this.OFFSET_ID=3,this.OFFSET_PARENT=4,this.OFFSET_NUM=5,this.OFFSET_PROP=6,this.ZOOM_BITS=5,this.mapping=new Map,this.geo_refs=new Map,this.key_refs=new Map,this.cluster_geo_refs=new Map,this.own=t,this.showLog=s,this.reduceType=this.isClusterReduce(),this.stride=this.reduceType!==S.NONE?7:6,this.reduceType&S.INNER&&(this.innerReduce=(t,e)=>{var s,i;this.weightKey&&(t.weight+=null!==(s=null==e?void 0:e.weight)&&void 0!==s?s:1),this.typeKey&&(t.divide_type=null!==(i=null==e?void 0:e.divide_type)&&void 0!==i?i:"unknown")},this.innerMap=t=>{var e,s,i,r;let n={};return this.weightKey&&("function"==typeof this.weightKey?n.weight=null!==(e=this.weightKey(t))&&void 0!==e?e:1:n.weight=t?null!==(s=t[this.weightKey])&&void 0!==s?s:1:null),this.typeKey&&("function"==typeof this.typeKey?n.divide_type=null!==(i=this.typeKey(t))&&void 0!==i?i:"unknown":n.divide_type=t?null!==(r=t[this.typeKey])&&void 0!==r?r:"unknown":null),n}),this.createZoomMapping(),this.own.on(e.DESTROY,(()=>{this.reset(),this.mapping.clear()}))}isClusterReduce(){let t=this.own.getOptions().clusterPointWeight,e=this.own.getOptions().clusterPointType;return t||e?(t&&(this.weightKey=t,this.isWeight=!0),e&&(this.typeKey=e),this.own.getOptions().clusterReduce?S.ALL:S.INNER):this.own.getOptions().clusterReduce?S.OUT:S.NONE}createZoomMapping(){let t=this.own.getOptions().clusterType;t&&0!==t.length&&(t.forEach((t=>{let[e,i,r,n,o]=t;if(i=null!=i?i:this.own.getOptions().clusterMaxZoom,r===s.GEO_FENCE||r===s.ATTR_REF)this.mapping.set(e,[null!=o?o:i,r,n]);else if(o)this.mapping.set(e,[o,s.DIS_PIXEL,n]);else for(var l=e;l<=i;l++)this.mapping.set(l,[l,s.DIS_PIXEL,n])})),this.showLog&&console.log("this.mapping",this.mapping))}getClusterType(t){let e,i=s.DIS_PIXEL;for(let[r,n]of[...this.mapping.entries()].reverse())if(r<=t){i=n[1],e=i===s.ATTR_REF&&n[2]instanceof Array?n[2][n[2].length-1]:n[2];break}return{type:i,name:e}}getClusterZoom(t){let e=this._limitZoom(t);for(let[s]of[...this.mapping.entries()].reverse())if(s<=t){e=s;break}return e}getClusterTree(t){let e=this.getClusterZoom(t);const{clusterMaxZoom:s=21}=this.own.getOptions();e{if(t.geometry){const[e,s]=t.geometry.coordinates;if(!isNaN(e)&&!isNaN(s)&&e>=-180&&e<=180&&s>=-90&&s<=90)return!0}return!1}));const{clusterMinZoom:e=3,clusterMaxZoom:i=21}=this.own.getOptions(),r=`prepare ${t.length} points`;this.showLog&&console.time(r);const n=[];for(let t=0;t=e;t--){const e=+Date.now(),i=this.getClusterZoom(t);if(this.mapping.has(i)){if(this.trees[i])continue;let t,[e,r,n]=this.mapping.get(i);switch(r){case s.GEO_FENCE:t=this._fence_cluster(e,n);break;case s.ATTR_REF:t=this._attribute_cluster(e,n);break;default:t=this._distance_cluster(o,e,n)}o=this.trees[i]=this._createTree(t)}else o=this.trees[i]=this._createTree(this._distance_cluster(o,i));this.showLog&&console.log("z%d: %d clusters in %dms",t,o.numItems,+Date.now()-e)}this.showLog&&console.timeEnd(r)}_createTree(t){const e=new o(t.length/this.stride|0,this.nodeSize,Float32Array);for(let s=0;s{if(!t.properties)return null;let i=null;if(e instanceof Array?e.forEach((e=>{var s;(null===(s=t.properties)||void 0===s?void 0:s.hasOwnProperty(e))&&(null===i?i=t.properties[e]:i+=this.splitChar+t.properties[e])})):i=t.properties[e],this.own.getOptions().clusterDictionary&&i&&!this.geo_refs.has(i)){let t=this.own.getOptions().clusterDictionary(s.ATTR_REF,i);t&&t.point&&this.geo_refs.set(i,t)}return i}),{zoom:t,type:s.ATTR_REF});this.showLog&&console.log("attribute_cluster",t,i);return this._match_cluster(s.ATTR_REF,i,t)}_fence_cluster(t,e){let i=new Map;if(this.own.getOptions().clusterDictionary){let t=e;t instanceof Array||(t=[t]),t.forEach((t=>{if(this.geo_refs.has(t))i.set(t,this.geo_refs.get(t));else{this.showLog&&console.log("fence_cluster_key",t);let e=this.own.getOptions().clusterDictionary(s.GEO_FENCE,t);e&&e.region&&(e.point||(e.point=v(e.region)),this.geo_refs.set(t,e),i.set(t,e))}}))}let r=O(this.points,(t=>((t,e,s)=>{let i=null;for(let[r,n]of t)if(y(e,s(n))){i=r;break}return i})(i,t,(t=>({type:"Feature",geometry:{type:"Polygon",coordinates:[t.region]}})))),{zoom:t,type:s.GEO_FENCE});this.showLog&&console.log("fence_cluster",r);return this._match_cluster(s.GEO_FENCE,r,t)}_match_cluster(t,e,i){let r=[];const{clusterMinPoints:n=3,clusterReduce:o}=this.own.getOptions();for(const l in e)if(e.hasOwnProperty(l)){let h=e[l];if("undefined"===l||h.length{var e;const[s,i]=t.geometry.coordinates,n=s,o=i;r.push(n,o,1/0,+(null!==(e=t.id)&&void 0!==e?e:0),-1,1),this.reduceType!==S.NONE&&r.push(-1)}));else{let e,a=-1;h.forEach((t=>{if(this.reduceType!==S.NONE){let s={};if(this.reduceType&S.INNER&&(s=this.innerMap(t.properties)),o){let e=this.own.getOptions().clusterMap(t.properties);Object.assign(s,e)}e?(this.reduceType&S.INNER&&this.innerReduce(e,Object.assign({},s)),o&&o(e,Object.assign({},s))):(e=Object.assign({},s),a=this.clusterProps.length,this.clusterProps.push(e))}}));let u=(h[0].id<1&&(u=h[0].id),r.push(s[0],s[1],1/0,u,-1,1)):r.push(s[0],s[1],1/0,u,-1,h.length),this.cluster_geo_refs.set(u,{bbox:e})}this.reduceType!==S.NONE&&r.push(a)}}return r}_distance_cluster(t,e,s){var i,r,n,o,l,h,a,u;const{clusterRadius:p=60,tileSize:c=256,clusterMinPoints:g=3,clusterReduce:d}=this.own.getOptions();const y=(void 0===s?p:s)/(c*Math.pow(2,e-1)),m=t.data,f=[],_=this.stride;for(let s=0;s{var s,i;const r=e*_;let n=null!==(i=null===(s=this._map(m,r))||void 0===s?void 0:s.divide_type)&&void 0!==i?i:"unknown";return n!=t&&this.showLog&&console.log("delete neighborId",`${t}!==${n}`),n===t}))}this.showLog&&console.log("delete neighborId end",w);const E=m[s+this.OFFSET_NUM];let O=E;const T=this.isWeight&&null!==(o=null===(n=this.clusterProps[m[s+this.OFFSET_PROP]])||void 0===n?void 0:n.weight)&&void 0!==o?o:1;let v=T;for(const t of w){const s=t*_;m[s+this.OFFSET_ZOOM]>e&&(O+=m[s+this.OFFSET_NUM],v+=this.isWeight&&null!==(h=null===(l=this.clusterProps[m[s+this.OFFSET_PROP]])||void 0===l?void 0:l.weight)&&void 0!==h?h:1)}if(O>E&&O>=g){let t,i=p*(this.isWeight?T:E),r=c*(this.isWeight?T:E),n=-1,o=[[p,c]];const l=(s/_<1)for(const t of w){const s=t*_;if(!(m[s+this.OFFSET_ZOOM]<=e)){m[s+this.OFFSET_ZOOM]=e;for(let t=0;t<_;t++)f.push(m[s+t])}}}}return f}_getOriginId(t){return t-this.points.length>>this.ZOOM_BITS}_getOriginZoom(t){return(t-this.points.length)%32}_map(t,e,s){if(t[e+this.OFFSET_NUM]>1){const i=this.clusterProps[t[e+this.OFFSET_PROP]];return s?Object.assign({},i):i}const i=this.points[t[e+this.OFFSET_ID]].properties;let r={};if(this.reduceType&S.INNER&&(r=this.innerMap(i)),this.own.getOptions().clusterReduce){let t=this.own.getOptions().clusterMap(i);Object.assign(r,t)}return s&&r===i?Object.assign({},r):r}_limitZoom(t){var e,s;let i=this.own.getOptions();return Math.max(null!==(e=i.clusterMinZoom)&&void 0!==e?e:3,Math.min(Math.floor(+t),(null!==(s=i.clusterMaxZoom)&&void 0!==s?s:21)+1))}getClusters(t,e){var i;if(this.isIllegal(t))return[[],[]];let r=((e[0]+180)%360+360)%360-180;const n=Math.max(-90,Math.min(90,e[1]));let o=180===e[2]?180:((e[2]+180)%360+360)%360-180;const l=Math.max(-90,Math.min(90,e[3]));if(e[2]-e[0]>=360)r=-180,o=180;else if(r>o){const e=this.getClusters(t,[r,n,180,l]),s=this.getClusters(t,[-180,n,o,l]);return[[...e[0],...s[0]],[...e[1],...s[1]]]}let h=this.getClusterZoom(t);const a=this.getClusterTree(t);if(!a)return[[],[]];const{type:u,name:p}=this.getClusterType(t),c=a.range(F(r),C(l),F(o),C(n)),g=a.data,d=[],y=[];this.showLog&&console.log("getClusters",c);for(const t of c){const e=this.stride*t;if(this.showLog&&console.log("getClusters",t,g.slice(e,e+this.stride)),g[e+this.OFFSET_NUM]>=this.own.getOptions().clusterMinPoints){let r=this.getClusterJSON(g,e,this.clusterProps);r.properties||(r.properties={}),r.properties.listChildren=this.getLeaves(g[e+this.OFFSET_ID],[],null!==(i=this.own.getOptions().clusterListChildren)&&void 0!==i?i:-1),r.properties.clusterIndex=(t<{var s,i;return(null===(s=t.properties)||void 0===s?void 0:s.pointCount)-(null===(i=e.properties)||void 0===i?void 0:i.pointCount)})),[y,d]}getElementById(t,e,i){let r=i?e>>this.ZOOM_BITS:e,n=new x;n.id=t,n.index=r;let o=i?e&(1<!!t&&t["_inner_"+e+"_"+i]===l))),h.slice(0,r).map((t=>{let s=new x;return s.id=t.id,s.index=t.id,s.type=e,s.isCluster=!1,s.properties=this.points[t.id].properties,s.latLng=this.points[t.id].geometry.coordinates,s}))}getLeaves(t,e=[],i){if(void 0===i&&(i=1/0),i<=0)return[];e||(e=[]);let r=this._getOriginZoom(t)-1,{type:n,name:o}=this.getClusterType(r);if(n===s.GEO_FENCE||n===s.ATTR_REF)return e=this.getUnDistanceBrothers(t,n,o,r,i);if(t>this.points.length){let s=this.getClusterTree(r+1),n=s.data;if(s)for(let s=0;s=1e4?`${Math.round(i/1e3)}k`:i>=1e3?Math.round(i/100)/10+"k":i;let n={};if(this.reduceType!==S.NONE){const i=t[e+this.OFFSET_PROP];n=-1===i?{}:Object.assign({},{reduces:s[i]})}return Object.assign(n,{isCluster:!0,clusterId:t[e+this.OFFSET_ID],parentId:t[e+this.OFFSET_PARENT],point:[t[e],t[e+1]],pointCount:i,pointCountAbbrev:r})}isIllegal(t){var e,s;let i=null!==(e=this.own.getOptions().minZoom)&&void 0!==e?e:3,r=null!==(s=this.own.getOptions().maxZoom)&&void 0!==s?s:23;return tr}reset(){this.points=[],this.clusterProps=[],this.trees={},this.geo_refs.clear(),this.key_refs.clear(),this.cluster_geo_refs.clear()}}class I{constructor(t,e,s,i){this._wait=600,this._fitViewMargin=[12,12,12,12],this.own=t,this.engine=e,this.map=s,this.showLog=i,this.register(),this.createLayers()}register(){this.own.getOptions().updateRealTime?(this._wait=this.own.getOptions().waitTime||300,this.map.addEventListener("update",this._onMapStatusChange=_(this.mapStatusChange.bind(this),this._wait,{leading:!0,trailing:!1}))):this.map.addEventListener("mapstatusidle_inner",this._onMapStatusChange=this.mapStatusChange.bind(this)),this.map.addEventListener("destroy",(()=>{this.own.destroy()})),this.own.on(e.DESTROY,(()=>{this.destroy()}))}unregister(){this.own.getOptions().updateRealTime?this.map.removeEventListener("update",this._onMapStatusChange):this.map.removeEventListener("mapstatusidle_inner",this._onMapStatusChange),this._onMapStatusChange=()=>{}}mapStatusChange(t){let s=this.map.getZoom(),i=this.map.getBounds(),r=i.getSouthWest(),n=i.getNorthEast();this.showLog&&console.log("地图状态变化",s,[r.lng,r.lat,n.lng,n.lat]);let o=this.engine.getClusters(s,[r.lng,r.lat,n.lng,n.lat]);this.own.fire(e.CHANGE,o),this.own.isRender&&this.update(o)}update(t){if(this.showLog&&console.log("更新数据",t),this.own.getOptions().isAnimation){if(!(this.multi_layer instanceof BMapGL.CustomHtmlLayer))throw new Error("isAnimation is true, but renderClusterStyle is not dom type");this.render([...t[0],...t[1]],r.MULTI)}else this.render(t[0],r.SINGLE),this.render(t[1],r.MULTI)}render(t,e){this.showLog&&console.log("渲染type",e),this.showLog&&console.log("渲染data",t);let s={type:"FeatureCollection",features:t},i=e===r.MULTI?this.multi_layer:this.single_layer;i&&(t.length>0&&(null==i||i.setData(s)),0===t.length&&(null==i||i.clearData()))}createLayers(){var t,e;(null===(t=this.own.getOptions().renderSingleStyle)||void 0===t?void 0:t.type)===i.DOM?this.single_layer=this.createLayer(r.SINGLE,i.DOM):this.single_layer=this.createLayer(r.SINGLE,i.WEBGL),(null===(e=this.own.getOptions().renderClusterStyle)||void 0===e?void 0:e.type)===i.DOM?this.multi_layer=this.createLayer(r.MULTI,i.DOM):this.multi_layer=this.createLayer(r.MULTI,i.WEBGL)}createLayer(t,s){this.showLog&&console.log("创建图层",s);let n,o=this.own.getOptions(),l=t===r.MULTI?o.renderClusterStyle:o.renderSingleStyle;if(!l)return;if(s===i.DOM){let e={sliceRepeat:!0,minZoom:this.own.getOptions().minZoom,maxZoom:this.own.getOptions().maxZoom,zIndex:t===r.MULTI?10:9,nextTick:!0,fixBottom:!0,useTranslate:!!this.own.getOptions().isAnimation,anchors:[.5,.5],displayType:"normal",enableDraggingMap:!0};this.own.getOptions().isAnimation&&(e.displayType="cluster"),Object.assign(e,l.style);const s=new BMapGL.CustomHtmlLayer(l.inject,e);this.map.addCustomHtmlLayer(s),n=s}else{let e=1.2*(o.clusterRadius||32),s={iconObj:(t,e)=>({canvas:(null==l?void 0:l.inject)(e),id:e.clusterId}),sizes:[e,e/2],scale:1,userSizes:!1,anchors:[0,0],width:e,height:e/2};Object.assign(s,l.style);let i={icon:"https://webmap0.bdimg.com/image/api/marker_red.png",sizes:[8,8],anchors:[0,-1],userSizes:!1,width:["match",["get","type"],0,16,8],height:["match",["get","type"],0,16,8]};Object.assign(i,l.style);const h=new BMapGL.PointIconLayer({minZoom:this.own.getOptions().minZoom,maxZoom:this.own.getOptions().maxZoom,zIndex:t===r.MULTI?10:9,isTop:!1,enablePicked:!0,autoSelect:!1,pickWidth:30,pickHeight:30,opacity:1,isFlat:!1,isFixed:!0,style:t===r.MULTI?s:i});this.map.addNormalLayer(h),n=h}n.addEventListener("click",(t=>{if(this.showLog&&console.log("鼠标点击事件",t),t.target instanceof BMapGL.PointIconLayer&&!t.value.dataItem)return;let s=this.getElementByEvent(t);s.bbox&&Array.isArray(s.bbox)&&s.bbox[0]!==1/0&&this.fitView(s.bbox),this.own.fire(e.CLICK,s)}));let h=s===i.DOM?"mouseover":"mousemove";return n.addEventListener(h,(t=>{if(this.showLog&&console.log("鼠标悬浮事件",t),t.target instanceof BMapGL.PointIconLayer&&!t.value.dataItem)return void(this._preMouseMove&&(this.own.fire(e.MOUSE_OUT,this._preMouseMove),this._preMouseMove=null));let s=this.getElementByEvent(t);if(t.target instanceof BMapGL.PointIconLayer)return this._preMouseMove&&this._preMouseMove.id===s.id||this.own.fire(e.MOUSE_OVER,s),void(this._preMouseMove=s);this.own.fire(e.MOUSE_OVER,s)})),n.addEventListener("mouseout",(t=>{if(this.showLog&&console.log("鼠标移开事件",t),t.target instanceof BMapGL.PointIconLayer)return;let s=this.getElementByEvent(t);this.own.fire(e.MOUSE_OUT,s)})),n}getElementByEvent(t){var e,s;let i,r,n=[];if(t.target instanceof BMapGL.CustomOverlay){let s=t.target.properties||{};i=s.clusterId,r=s.clusterIndex,n=null!==(e=s.listChildren)&&void 0!==e?e:[]}else{let e=t.value.dataItem.properties||{};i=e.clusterId,r=e.clusterIndex,n=null!==(s=e.listChildren)&&void 0!==s?s:[]}let o=this.engine.getElementById(i,r,!0);return o.listChildren=n,o.pixel=[t.pixel.x,t.pixel.y],o.target=t.target,o}clearRender(){this.showLog&&console.log("清除图层"),this.update([[],[]])}unrender(){this.showLog&&console.log("销毁图层"),this.multi_layer instanceof BMapGL.PointIconLayer?this.map.removeNormalLayer(this.multi_layer):this.map.removeCustomHtmlLayer(this.multi_layer),this.single_layer instanceof BMapGL.PointIconLayer?this.map.removeNormalLayer(this.single_layer):this.map.removeCustomHtmlLayer(this.single_layer),this.multi_layer=void 0,this.single_layer=void 0}fitView(t){if(this.showLog&&console.log("fitView",t),this.own.getOptions().fitViewOnClick){let e=this.map.getZoom(),s=[new BMapGL.Point(t[0],t[1]),new BMapGL.Point(t[2],t[3])],i={margins:this._fitViewMargin,enableAnimation:!0};this.map.setViewport(s,i),setTimeout((()=>{e===this.map.getZoom()&&this.map.setZoom(e+1),this.mapStatusChange()}),300)}}show(){this.single_layer&&(this.single_layer instanceof BMapGL.CustomHtmlLayer&&this.single_layer.show(),this.single_layer instanceof BMapGL.PointIconLayer&&this.single_layer.setVisible(!0)),this.multi_layer&&(this.multi_layer instanceof BMapGL.CustomHtmlLayer&&this.multi_layer.show(),this.multi_layer instanceof BMapGL.PointIconLayer&&this.multi_layer.setVisible(!0))}hide(){this.single_layer&&(this.single_layer instanceof BMapGL.CustomHtmlLayer&&this.single_layer.hide(),this.single_layer instanceof BMapGL.PointIconLayer&&this.single_layer.setVisible(!1)),this.multi_layer&&(this.multi_layer instanceof BMapGL.CustomHtmlLayer&&this.multi_layer.hide(),this.multi_layer instanceof BMapGL.PointIconLayer&&this.multi_layer.setVisible(!1))}getSingleLayer(){return this.single_layer}getClusterLayer(){return this.multi_layer}drawMarker(t){this.map.addOverlay(new BMapGL.Marker(new BMapGL.Point(t[0],t[1])))}destroy(){this.map&&(this.unregister(),this.unrender())}}var N=Object.freeze({__proto__:null,get ClusterData(){return r},ClusterElement:x,get ClusterEvent(){return e},get ClusterRender(){return i},get ClusterType(){return s},View:class extends t{constructor(t,e){if(super(),this._showLog=!1,!t)throw new Error("map is required");e&&this.verifyOptions(e);const s={tileSize:256,minZoom:3,maxZoom:21,clusterRadius:30,clusterMinZoom:3,clusterMaxZoom:16,clusterMinPoints:3,clusterListChildren:0,clusterPointWeight:void 0,clusterPointType:void 0,clusterMap:t=>({}),clusterReduce:void 0,clusterType:void 0,clusterDictionary:void 0,isRender:!0,isAnimation:!1,renderClusterStyle:{type:i.DOM,style:{},inject:t=>{var e=Math.pow(t.pointCount/t.allCount,.1),s=document.createElement("div"),i=180-180*e,r="hsla("+i+",100%,30%,0.7)",n="hsla("+i+",100%,90%,1)",o="hsla("+i+",100%,30%,1)",l="hsla("+i+",100%,90%,1)";s.style.backgroundColor=r;var h=Math.round(25+30*Math.pow(t.pointCount/t.allCount,1/6));return s.style.width=s.style.height=h+"px",s.style.border="solid 1px "+o,s.style.borderRadius=h/2+"px",s.style.boxShadow="0 0 5px "+l,s.innerHTML=t.pointCount,s.style.lineHeight=h+"px",s.style.color=n,s.style.fontSize="14px",s.style.textAlign="center",s.style.cursor="pointer",s}},renderSingleStyle:{type:i.WEBGL,style:{},inject:t=>document.createElement("canvas")},fitViewOnClick:!0,updateRealTime:!1,waitTime:300};this.options=Object.assign(s,e),this.engine=new b(this,this._showLog),this.render=new I(this,this.engine,t,this._showLog),this.isRender=this.options.isRender||!1}setData(t){this._showLog&&console.log("cluster options",this.options),this.engine.createClusters(t),this.redraw()}getSingleLayer(){return this.render.getSingleLayer()}getClusterLayer(){return this.render.getClusterLayer()}getLeaves(t,e){return this.engine.getLeaves(t,[],e)}getSonNodes(t){return this.engine.getChildNodes(t)}redraw(){this.render.mapStatusChange()}show(){this.render.show()}hide(){this.render.hide()}destroy(){this.fire(e.DESTROY)}get isRender(){return this._isRender}set isRender(t){this._isRender=t,!t&&this.render.clearRender()}getOptions(){return this.options}verifyOptions(t){var e,s;if(t.minZoom&&t.minZoom<3)throw new Error("minZoom must be greater than 3");if(t.maxZoom&&t.maxZoom>23)throw new Error("maxZoom must be less than 23");if(Math.max(null!==(e=t.minZoom)&&void 0!==e?e:3,3)>Math.min(null!==(s=t.maxZoom)&&void 0!==s?s:23,23))throw new Error("minZoom must be less than maxZoom");if(t.clusterMinZoom&&t.clusterMinZoom<3)throw new Error("clusterMinZoom must be greater than 3");if(t.clusterMaxZoom&&t.clusterMaxZoom>23)throw new Error("clusterMaxZoom must be less than 23");if(t.clusterMinZoom&&t.clusterMaxZoom&&t.clusterMinZoom>t.clusterMaxZoom)throw new Error("clusterMinZoom must be less than clusterMaxZoom");if(t.clusterType){if(!(t.clusterType instanceof Array))throw new Error("clusterType must be an array");if(t.clusterType.length<1)throw new Error("clusterType must be greater than 0");for(let e=0;et.clusterType[e][1])throw new Error("clusterType item endZoom must be less than starZoom");if(null===t.clusterType[e][1]&&et.clusterType[e+1][0])throw new Error("clusterType item endZoom must be less than the next startZoom")}}}},pointTransformer:(t,e)=>{let s=[];return t.forEach((t=>{const{point:i=null,properties:r={}}=e(t);i&&s.push({type:"Feature",geometry:{type:"Point",coordinates:i},properties:r})})),s}});return N}));
\ No newline at end of file
diff --git a/media/js/map/marker-cluster.js b/media/js/map/marker-cluster.js
deleted file mode 100644
index 42fc369509..0000000000
--- a/media/js/map/marker-cluster.js
+++ /dev/null
@@ -1,642 +0,0 @@
-/**
- * @fileoverview MarkerCluster标记聚合器用来解决加载大量点要素到地图上产生覆盖现象的问题,并提高性能。
- * 主入口类是MarkerCluster,
- * 基于Baidu Map API 1.2。
- *
- * @author Baidu Map Api Group
- * @version 1.2
- */
-
-/**
- * @namespace BMap的所有library类均放在BMapLib命名空间下
- */
-var BMapLib = window.BMapLib = BMapLib || {};
-(function(){
-
- /**
- * 获取一个扩展的视图范围,把上下左右都扩大一样的像素值。
- * @param {Map} map BMapGL.Map的实例化对象
- * @param {BMapGL.Bounds} bounds BMapGL.Bounds的实例化对象
- * @param {Number} gridSize 要扩大的像素值
- *
- * @return {BMapGL.Bounds} 返回扩大后的视图范围。
- */
- var getExtendedBounds = function(map, bounds, gridSize){
- bounds = cutBoundsInRange(bounds);
- var pixelNE = map.pointToPixel(bounds.getNorthEast());
- var pixelSW = map.pointToPixel(bounds.getSouthWest());
- pixelNE.x += gridSize;
- pixelNE.y -= gridSize;
- pixelSW.x -= gridSize;
- pixelSW.y += gridSize;
- var newNE = map.pixelToPoint(pixelNE);
- var newSW = map.pixelToPoint(pixelSW);
- if (!newSW || !newNE) return null;
- return new BMapGL.Bounds(newSW, newNE);
- };
-
- /**
- * 按照百度地图支持的世界范围对bounds进行边界处理
- * @param {BMapGL.Bounds} bounds BMapGL.Bounds的实例化对象
- *
- * @return {BMapGL.Bounds} 返回不越界的视图范围
- */
- var cutBoundsInRange = function (bounds) {
- var maxX = getRange(bounds.getNorthEast().lng, -180, 180);
- var minX = getRange(bounds.getSouthWest().lng, -180, 180);
- var maxY = getRange(bounds.getNorthEast().lat, -90, 90);
- var minY = getRange(bounds.getSouthWest().lat, -90, 90);
- return new BMapGL.Bounds(new BMapGL.Point(minX, minY), new BMapGL.Point(maxX, maxY));
- };
-
- /**
- * 对单个值进行边界处理。
- * @param {Number} i 要处理的数值
- * @param {Number} min 下边界值
- * @param {Number} max 上边界值
- *
- * @return {Number} 返回不越界的数值
- */
- var getRange = function (i, mix, max) {
- mix && (i = Math.max(i, mix));
- max && (i = Math.min(i, max));
- return i;
- };
-
- /**
- * 判断给定的对象是否为数组
- * @param {Object} source 要测试的对象
- *
- * @return {Boolean} 如果是数组返回true,否则返回false
- */
- var isArray = function (source) {
- return '[object Array]' === Object.prototype.toString.call(source);
- };
-
- /**
- * 返回item在source中的索引位置
- * @param {Object} item 要测试的对象
- * @param {Array} source 数组
- *
- * @return {Number} 如果在数组内,返回索引,否则返回-1
- */
- var indexOf = function(item, source){
- var index = -1;
- if(isArray(source)){
- if (source.indexOf) {
- index = source.indexOf(item);
- } else {
- for (var i = 0, m; m = source[i]; i++) {
- if (m === item) {
- index = i;
- break;
- }
- }
- }
- }
- return index;
- };
-
- /**
- *@exports MarkerCluster as BMapLib.MarkerCluster
- */
- var MarkerCluster =
- /**
- * MarkerCluster
- * @class 用来解决加载大量点要素到地图上产生覆盖现象的问题,并提高性能
- * @constructor
- * @param {Map} map 地图的一个实例。
- * @param {Json Object} options 可选参数,可选项包括:
- * markers {Array} 要聚合的标记数组
- * girdSize {Number} 聚合计算时网格的像素大小,默认60
- * maxZoom {Number} 最大的聚合级别,大于该级别就不进行相应的聚合
- * minClusterSize {Number} 最小的聚合数量,小于该数量的不能成为一个聚合,默认为2
- * isAvgCenter {Boolean} 聚合点的落脚位置是否是所有聚合在内点的平均值,默认为否,落脚在聚合内的第一个点
- * styles {Array} 自定义聚合后的图标风格,请参考TextIconOverlay类
- */
- BMapLib.MarkerCluster = function(map, options){
- if (!map){
- return;
- }
- this._map = map;
- this._markers = [];
- this._clusters = [];
-
- var opts = options || {};
- this._gridSize = opts["gridSize"] || 60;
- this._maxZoom = opts["maxZoom"] || 21;
- this._minClusterSize = opts["minClusterSize"] || 2;
- this._isAverageCenter = false;
- if (opts['isAverageCenter'] != undefined) {
- this._isAverageCenter = opts['isAverageCenter'];
- }
- this._styles = opts["styles"] || [];
- this._callback = opts["callback"] || function(){};
-
- var that = this;
- this._map.addEventListener("zoomend",function(){
- that._redraw();
- });
-
- // this._map.addEventListener("moveend",function(){
- // that._redraw();
- // });
-
- var markers = opts["markers"];
- isArray(markers) && this.addMarkers(markers);
- };
-
- /**
- * 添加要聚合的标记数组。
- * @param {Array} markers 要聚合的标记数组
- *
- * @return 无返回值。
- */
- MarkerCluster.prototype.addMarkers = function(markers){
- for(var i = 0, len = markers.length; i } markers 需要被删除的marker数组
- *
- * @return {Boolean} 删除成功返回true,否则返回false
- */
- MarkerCluster.prototype.removeMarkers = function(markers) {
- var success = false;
- for (var i = 0; i < markers.length; i++) {
- var r = this._removeMarker(markers[i]);
- success = success || r;
- }
-
- if (success) {
- this._clearLastClusters();
- this._createClusters();
- }
- return success;
- };
-
- /**
- * 从地图上彻底清除所有的标记
- * @return 无返回值
- */
- MarkerCluster.prototype.clearMarkers = function() {
- this._clearLastClusters();
- this._removeMarkersFromMap();
- this._markers = [];
- };
-
- /**
- * 重新生成,比如改变了属性等
- * @return 无返回值
- */
- MarkerCluster.prototype._redraw = function () {
- this._clearLastClusters();
- this._createClusters();
- };
-
- /**
- * 获取网格大小
- * @return {Number} 网格大小
- */
- MarkerCluster.prototype.getGridSize = function() {
- return this._gridSize;
- };
-
- /**
- * 设置网格大小
- * @param {Number} size 网格大小
- * @return 无返回值
- */
- MarkerCluster.prototype.setGridSize = function(size) {
- this._gridSize = size;
- this._redraw();
- };
-
- /**
- * 获取聚合的最大缩放级别。
- * @return {Number} 聚合的最大缩放级别。
- */
- MarkerCluster.prototype.getMaxZoom = function() {
- return this._maxZoom;
- };
-
- /**
- * 设置聚合的最大缩放级别
- * @param {Number} maxZoom 聚合的最大缩放级别
- * @return 无返回值
- */
- MarkerCluster.prototype.setMaxZoom = function(maxZoom) {
- this._maxZoom = maxZoom;
- this._redraw();
- };
-
- /**
- * 获取聚合的样式风格集合
- * @return {Array} 聚合的样式风格集合
- */
- MarkerCluster.prototype.getStyles = function() {
- return this._styles;
- };
-
- /**
- * 设置聚合的样式风格集合
- * @param {Array} styles 样式风格数组
- * @return 无返回值
- */
- MarkerCluster.prototype.setStyles = function(styles) {
- this._styles = styles;
- this._redraw();
- };
-
- /**
- * 获取单个聚合的最小数量。
- * @return {Number} 单个聚合的最小数量。
- */
- MarkerCluster.prototype.getMinClusterSize = function() {
- return this._minClusterSize;
- };
-
- /**
- * 设置单个聚合的最小数量。
- * @param {Number} size 单个聚合的最小数量。
- * @return 无返回值。
- */
- MarkerCluster.prototype.setMinClusterSize = function(size) {
- this._minClusterSize = size;
- this._redraw();
- };
-
- /**
- * 获取单个聚合的落脚点是否是聚合内所有标记的平均中心。
- * @return {Boolean} true或false。
- */
- MarkerCluster.prototype.isAverageCenter = function() {
- return this._isAverageCenter;
- };
-
- /**
- * 获取聚合的Map实例。
- * @return {Map} Map的示例。
- */
- MarkerCluster.prototype.getMap = function() {
- return this._map;
- };
-
- /**
- * 获取所有的标记数组。
- * @return {Array} 标记数组。
- */
- MarkerCluster.prototype.getMarkers = function() {
- return this._markers;
- };
-
- /**
- * 获取聚合的总数量。
- * @return {Number} 聚合的总数量。
- */
- MarkerCluster.prototype.getClustersCount = function() {
- var count = 0;
- for(var i = 0, cluster; cluster = this._clusters[i]; i++){
- cluster.isReal() && count++;
- }
- return count;
- };
-
- MarkerCluster.prototype.getCallback = function() {
- return this._callback;
- }
-
- /**
- * @ignore
- * Cluster
- * @class 表示一个聚合对象,该聚合,包含有N个标记,这N个标记组成的范围,并有予以显示在Map上的TextIconOverlay等。
- * @constructor
- * @param {MarkerCluster} markerCluster 一个标记聚合器示例。
- */
- function Cluster(markerCluster){
- this._markerCluster = markerCluster;
- this._map = markerCluster.getMap();
- this._minClusterSize = markerCluster.getMinClusterSize();
- this._isAverageCenter = markerCluster.isAverageCenter();
- this._center = null;//落脚位置
- this._markers = [];//这个Cluster中所包含的markers
- this._gridBounds = null;//以中心点为准,向四边扩大gridSize个像素的范围,也即网格范围
- this._isReal = false; //真的是个聚合
-
- this._clusterMarker = new BMapLib.TextIconOverlay(this._center, this._markers.length, {"styles":this._markerCluster.getStyles()});
- //this._map.addOverlay(this._clusterMarker);
- }
-
- /**
- * 向该聚合添加一个标记。
- * @param {Marker} marker 要添加的标记。
- * @return 无返回值。
- */
- Cluster.prototype.addMarker = function(marker){
- if(this.isMarkerInCluster(marker)){
- return false;
- }//也可用marker.isInCluster判断,外面判断OK,这里基本不会命中
-
- if (!this._center){
- this._center = marker.getPosition();
- this.updateGridBounds();//
- } else {
- if(this._isAverageCenter){
- var l = this._markers.length + 1;
- var lat = (this._center.lat * (l - 1) + marker.getPosition().lat) / l;
- var lng = (this._center.lng * (l - 1) + marker.getPosition().lng) / l;
- this._center = new BMapGL.Point(lng, lat);
- this.updateGridBounds();
- }//计算新的Center
- }
-
- marker.isInCluster = true;
- this._markers.push(marker);
- };
-
- /**
- * 进行dom操作
- * @return 无返回值
- */
- Cluster.prototype.render = function(){
- var len = this._markers.length;
-
- if (len < this._minClusterSize) {
- for (var i = 0; i < len; i++) {
- this._map.addOverlay(this._markers[i]);
- }
- } else {
- this._map.addOverlay(this._clusterMarker);
- this._isReal = true;
- this.updateClusterMarker();
- }
- }
-
- /**
- * 判断一个标记是否在该聚合中。
- * @param {Marker} marker 要判断的标记。
- * @return {Boolean} true或false。
- */
- Cluster.prototype.isMarkerInCluster= function(marker){
- if (this._markers.indexOf) {
- return this._markers.indexOf(marker) != -1;
- } else {
- for (var i = 0, m; m = this._markers[i]; i++) {
- if (m === marker) {
- return true;
- }
- }
- }
- return false;
- };
-
- /**
- * 判断一个标记是否在该聚合网格范围中。
- * @param {Marker} marker 要判断的标记。
- * @return {Boolean} true或false。
- */
- Cluster.prototype.isMarkerInClusterBounds = function(marker) {
- return this._gridBounds.containsPoint(marker.getPosition());
- };
-
- Cluster.prototype.isReal = function(marker) {
- return this._isReal;
- };
-
- /**
- * 更新该聚合的网格范围。
- * @return 无返回值。
- */
- Cluster.prototype.updateGridBounds = function() {
- var bounds = new BMapGL.Bounds(this._center, this._center);
- this._gridBounds = getExtendedBounds(this._map, bounds, this._markerCluster.getGridSize());
- };
-
- /**
- * 更新该聚合的显示样式,也即TextIconOverlay。
- * @return 无返回值。
- */
- Cluster.prototype.updateClusterMarker = function () {
- if (this._map.getZoom() > this._markerCluster.getMaxZoom()) {
- this._clusterMarker && this._map.removeOverlay(this._clusterMarker);
- for (var i = 0, marker; marker = this._markers[i]; i++) {
- this._map.addOverlay(marker);
- }
- return;
- }
-
- if (this._markers.length < this._minClusterSize) {
- this._clusterMarker.hide();
- return;
- }
-
- this._clusterMarker.setPosition(this._center);
-
- // this._clusterMarker.setText(this._markers.length);
- var imageUrl = this._markers[0].getImageUrl();
- this._clusterMarker.setText(this._markers.length, imageUrl);
-
- var thatMap = this._map;
- var thatBounds = this.getBounds();
- let clickTimeout;
- this._clusterMarker.addEventListener("click", (event) => {
- if (clickTimeout) {
- clearTimeout(clickTimeout);
- clickTimeout = null;
- return;
- }
- clickTimeout = setTimeout(() => {
- if (this._markerCluster && typeof this._markerCluster.getCallback() === 'function') {
- const markers = this._markers;
- this._markerCluster.getCallback()(event, markers);
- }
- clickTimeout = null;
- }, 300); // Delay to differentiate between single and double click
- });
-
- this._clusterMarker.addEventListener("dblclick", (event) => {
- if (clickTimeout) {
- clearTimeout(clickTimeout);
- clickTimeout = null;
- }
- // Do nothing on double click
- });
- };
-
- /**
- * 删除该聚合。
- * @return 无返回值。
- */
- Cluster.prototype.remove = function(){
- for (var i = 0, m; m = this._markers[i]; i++) {
- this._markers[i].getMap() && this._map.removeOverlay(this._markers[i])
- }//清除散的标记点
- this._map.removeOverlay(this._clusterMarker);
- this._markers.length = 0;
- delete this._markers;
- }
-
- /**
- * 获取该聚合所包含的所有标记的最小外接矩形的范围。
- * @return {BMapGL.Bounds} 计算出的范围。
- */
- Cluster.prototype.getBounds = function() {
- var bounds = new BMapGL.Bounds(this._center, this._center);
- for (var i = 0, marker; marker = this._markers[i]; i++) {
- bounds.extend(marker.getPosition());
- }
- return bounds;
- };
-
- /**
- * 获取该聚合的落脚点。
- * @return {BMapGL.Point} 该聚合的落脚点。
- */
- Cluster.prototype.getCenter = function() {
- return this._center;
- };
-
-})();
diff --git a/media/js/map/text-icon-overlay.js b/media/js/map/text-icon-overlay.js
deleted file mode 100644
index c713ea3670..0000000000
--- a/media/js/map/text-icon-overlay.js
+++ /dev/null
@@ -1,1077 +0,0 @@
-/**
- * @fileoverview 此类表示地图上的一个覆盖物,该覆盖物由文字和图标组成,从Overlay继承。
- * 主入口类是TextIconOverlay,
- * 基于Baidu Map API 1.2。
- *
- * @author Baidu Map Api Group
- * @version 1.2
- */
-
-
- /**
- * @namespace BMap的所有library类均放在BMapLib命名空间下
- */
-var BMapLib = window.BMapLib = BMapLib || {};
-
-(function () {
-
- /**
- * 声明baidu包
- */
- var T,
- baidu = T = baidu || {version: "1.3.8"};
-
- (function (){
- //提出guid,防止在与老版本Tangram混用时
- //在下一行错误的修改window[undefined]
- baidu.guid = "$BAIDU$";
-
- //Tangram可能被放在闭包中
- //一些页面级别唯一的属性,需要挂载在window[baidu.guid]上
- window[baidu.guid] = window[baidu.guid] || {};
-
- /**
- * @ignore
- * @namespace baidu.dom 操作dom的方法。
- */
- baidu.dom = baidu.dom || {};
-
-
- /**
- * 从文档中获取指定的DOM元素
- * @name baidu.dom.g
- * @function
- * @grammar baidu.dom.g(id)
- * @param {string|HTMLElement} id 元素的id或DOM元素
- * @shortcut g,T.G
- * @meta standard
- * @see baidu.dom.q
- *
- * @returns {HTMLElement|null} 获取的元素,查找不到时返回null,如果参数不合法,直接返回参数
- */
- baidu.dom.g = function (id) {
- if ('string' == typeof id || id instanceof String) {
- return document.getElementById(id);
- } else if (id && id.nodeName && (id.nodeType == 1 || id.nodeType == 9)) {
- return id;
- }
- return null;
- };
-
- // 声明快捷方法
- baidu.g = baidu.G = baidu.dom.g;
-
- /**
- * 获取目标元素所属的document对象
- * @name baidu.dom.getDocument
- * @function
- * @grammar baidu.dom.getDocument(element)
- * @param {HTMLElement|string} element 目标元素或目标元素的id
- * @meta standard
- * @see baidu.dom.getWindow
- *
- * @returns {HTMLDocument} 目标元素所属的document对象
- */
- baidu.dom.getDocument = function (element) {
- element = baidu.dom.g(element);
- return element.nodeType == 9 ? element : element.ownerDocument || element.document;
- };
-
- /**
- * @ignore
- * @namespace baidu.lang 对语言层面的封装,包括类型判断、模块扩展、继承基类以及对象自定义事件的支持。
- */
- baidu.lang = baidu.lang || {};
-
- /**
- * 判断目标参数是否string类型或String对象
- * @name baidu.lang.isString
- * @function
- * @grammar baidu.lang.isString(source)
- * @param {Any} source 目标参数
- * @shortcut isString
- * @meta standard
- * @see baidu.lang.isObject,baidu.lang.isNumber,baidu.lang.isArray,baidu.lang.isElement,baidu.lang.isBoolean,baidu.lang.isDate
- *
- * @returns {boolean} 类型判断结果
- */
- baidu.lang.isString = function (source) {
- return '[object String]' == Object.prototype.toString.call(source);
- };
-
- // 声明快捷方法
- baidu.isString = baidu.lang.isString;
-
- /**
- * 从文档中获取指定的DOM元素
- * **内部方法**
- *
- * @param {string|HTMLElement} id 元素的id或DOM元素
- * @meta standard
- * @return {HTMLElement} DOM元素,如果不存在,返回null,如果参数不合法,直接返回参数
- */
- baidu.dom._g = function (id) {
- if (baidu.lang.isString(id)) {
- return document.getElementById(id);
- }
- return id;
- };
-
- // 声明快捷方法
- baidu._g = baidu.dom._g;
-
- /**
- * @ignore
- * @namespace baidu.browser 判断浏览器类型和特性的属性。
- */
- baidu.browser = baidu.browser || {};
-
- if (/msie (\d+\.\d)/i.test(navigator.userAgent)) {
- //IE 8下,以documentMode为准
- //在百度模板中,可能会有$,防止冲突,将$1 写成 \x241
- /**
- * 判断是否为ie浏览器
- * @property ie ie版本号
- * @grammar baidu.browser.ie
- * @meta standard
- * @shortcut ie
- * @see baidu.browser.firefox,baidu.browser.safari,baidu.browser.opera,baidu.browser.chrome,baidu.browser.maxthon
- */
- baidu.browser.ie = baidu.ie = document.documentMode || + RegExp['\x241'];
- }
-
- /**
- * 获取目标元素的computed style值。如果元素的样式值不能被浏览器计算,则会返回空字符串(IE)
- *
- * @author berg
- * @name baidu.dom.getComputedStyle
- * @function
- * @grammar baidu.dom.getComputedStyle(element, key)
- * @param {HTMLElement|string} element 目标元素或目标元素的id
- * @param {string} key 要获取的样式名
- *
- * @see baidu.dom.getStyle
- *
- * @returns {string} 目标元素的computed style值
- */
-
- baidu.dom.getComputedStyle = function(element, key){
- element = baidu.dom._g(element);
- var doc = baidu.dom.getDocument(element),
- styles;
- if (doc.defaultView && doc.defaultView.getComputedStyle) {
- styles = doc.defaultView.getComputedStyle(element, null);
- if (styles) {
- return styles[key] || styles.getPropertyValue(key);
- }
- }
- return '';
- };
-
- /**
- * 提供给setStyle与getStyle使用
- */
- baidu.dom._styleFixer = baidu.dom._styleFixer || {};
-
- /**
- * 提供给setStyle与getStyle使用
- */
- baidu.dom._styleFilter = baidu.dom._styleFilter || [];
-
- /**
- * 为获取和设置样式的过滤器
- * @private
- * @meta standard
- */
- baidu.dom._styleFilter.filter = function (key, value, method) {
- for (var i = 0, filters = baidu.dom._styleFilter, filter; filter = filters[i]; i++) {
- if (filter = filter[method]) {
- value = filter(key, value);
- }
- }
- return value;
- };
-
- /**
- * @ignore
- * @namespace baidu.string 操作字符串的方法。
- */
- baidu.string = baidu.string || {};
-
- /**
- * 将目标字符串进行驼峰化处理
- * @name baidu.string.toCamelCase
- * @function
- * @grammar baidu.string.toCamelCase(source)
- * @param {string} source 目标字符串
- * @remark
- * 支持单词以“-_”分隔
- * @meta standard
- *
- * @returns {string} 驼峰化处理后的字符串
- */
- baidu.string.toCamelCase = function (source) {
- //提前判断,提高getStyle等的效率
- if (source.indexOf('-') < 0 && source.indexOf('_') < 0) {
- return source;
- }
- return source.replace(/[-_][^-_]/g, function (match) {
- return match.charAt(1).toUpperCase();
- });
- };
-
- /**
- * 获取目标元素的样式值
- * @name baidu.dom.getStyle
- * @function
- * @grammar baidu.dom.getStyle(element, key)
- * @param {HTMLElement|string} element 目标元素或目标元素的id
- * @param {string} key 要获取的样式名
- * @remark
- *
- * 为了精简代码,本模块默认不对任何浏览器返回值进行归一化处理(如使用getStyle时,不同浏览器下可能返回rgb颜色或hex颜色),也不会修复浏览器的bug和差异性(如设置IE的float属性叫styleFloat,firefox则是cssFloat)。
- * baidu.dom._styleFixer和baidu.dom._styleFilter可以为本模块提供支持。
- * 其中_styleFilter能对颜色和px进行归一化处理,_styleFixer能对display,float,opacity,textOverflow的浏览器兼容性bug进行处理。
- * @shortcut getStyle
- * @meta standard
- * @see baidu.dom.setStyle,baidu.dom.setStyles, baidu.dom.getComputedStyle
- *
- * @returns {string} 目标元素的样式值
- */
- baidu.dom.getStyle = function (element, key) {
- var dom = baidu.dom;
-
- element = dom.g(element);
- key = baidu.string.toCamelCase(key);
- //computed style, then cascaded style, then explicitly set style.
- var value = element.style[key] ||
- (element.currentStyle ? element.currentStyle[key] : "") ||
- dom.getComputedStyle(element, key);
-
- // 在取不到值的时候,用fixer进行修正
- if (!value) {
- var fixer = dom._styleFixer[key];
- if(fixer){
- value = fixer.get ? fixer.get(element) : baidu.dom.getStyle(element, fixer);
- }
- }
-
- /* 检查结果过滤器 */
- if (fixer = dom._styleFilter) {
- value = fixer.filter(key, value, 'get');
- }
-
- return value;
- };
-
- // 声明快捷方法
- baidu.getStyle = baidu.dom.getStyle;
-
-
- if (/opera\/(\d+\.\d)/i.test(navigator.userAgent)) {
- /**
- * 判断是否为opera浏览器
- * @property opera opera版本号
- * @grammar baidu.browser.opera
- * @meta standard
- * @see baidu.browser.ie,baidu.browser.firefox,baidu.browser.safari,baidu.browser.chrome
- */
- baidu.browser.opera = + RegExp['\x241'];
- }
-
- /**
- * 判断是否为webkit内核
- * @property isWebkit
- * @grammar baidu.browser.isWebkit
- * @meta standard
- * @see baidu.browser.isGecko
- */
- baidu.browser.isWebkit = /webkit/i.test(navigator.userAgent);
-
- /**
- * 判断是否为gecko内核
- * @property isGecko
- * @grammar baidu.browser.isGecko
- * @meta standard
- * @see baidu.browser.isWebkit
- */
- baidu.browser.isGecko = /gecko/i.test(navigator.userAgent) && !/like gecko/i.test(navigator.userAgent);
-
- /**
- * 判断是否严格标准的渲染模式
- * @property isStrict
- * @grammar baidu.browser.isStrict
- * @meta standard
- */
- baidu.browser.isStrict = document.compatMode == "CSS1Compat";
-
- /**
- * 获取目标元素相对于整个文档左上角的位置
- * @name baidu.dom.getPosition
- * @function
- * @grammar baidu.dom.getPosition(element)
- * @param {HTMLElement|string} element 目标元素或目标元素的id
- * @meta standard
- *
- * @returns {Object} 目标元素的位置,键值为top和left的Object。
- */
- baidu.dom.getPosition = function (element) {
- element = baidu.dom.g(element);
- var doc = baidu.dom.getDocument(element),
- browser = baidu.browser,
- getStyle = baidu.dom.getStyle,
- // Gecko 1.9版本以下用getBoxObjectFor计算位置
- // 但是某些情况下是有bug的
- // 对于这些有bug的情况
- // 使用递归查找的方式
- BUGGY_GECKO_BOX_OBJECT = browser.isGecko > 0 &&
- doc.getBoxObjectFor &&
- getStyle(element, 'position') == 'absolute' &&
- (element.style.top === '' || element.style.left === ''),
- pos = {"left":0,"top":0},
- viewport = (browser.ie && !browser.isStrict) ? doc.body : doc.documentElement,
- parent,
- box;
-
- if(element == viewport){
- return pos;
- }
-
- if(element.getBoundingClientRect){ // IE and Gecko 1.9+
-
- //当HTML或者BODY有border width时, 原生的getBoundingClientRect返回值是不符合预期的
- //考虑到通常情况下 HTML和BODY的border只会设成0px,所以忽略该问题.
- box = element.getBoundingClientRect();
-
- pos.left = Math.floor(box.left) + Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft);
- pos.top = Math.floor(box.top) + Math.max(doc.documentElement.scrollTop, doc.body.scrollTop);
-
- // IE会给HTML元素添加一个border,默认是medium(2px)
- // 但是在IE 6 7 的怪异模式下,可以被html { border: 0; } 这条css规则覆盖
- // 在IE7的标准模式下,border永远是2px,这个值通过clientLeft 和 clientTop取得
- // 但是。。。在IE 6 7的怪异模式,如果用户使用css覆盖了默认的medium
- // clientTop和clientLeft不会更新
- pos.left -= doc.documentElement.clientLeft;
- pos.top -= doc.documentElement.clientTop;
-
- var htmlDom = doc.body,
- // 在这里,不使用element.style.borderLeftWidth,只有computedStyle是可信的
- htmlBorderLeftWidth = parseInt(getStyle(htmlDom, 'borderLeftWidth')),
- htmlBorderTopWidth = parseInt(getStyle(htmlDom, 'borderTopWidth'));
- if(browser.ie && !browser.isStrict){
- pos.left -= isNaN(htmlBorderLeftWidth) ? 2 : htmlBorderLeftWidth;
- pos.top -= isNaN(htmlBorderTopWidth) ? 2 : htmlBorderTopWidth;
- }
- } else {
- // safari/opera/firefox
- parent = element;
-
- do {
- pos.left += parent.offsetLeft;
- pos.top += parent.offsetTop;
-
- // safari里面,如果遍历到了一个fixed的元素,后面的offset都不准了
- if (browser.isWebkit > 0 && getStyle(parent, 'position') == 'fixed') {
- pos.left += doc.body.scrollLeft;
- pos.top += doc.body.scrollTop;
- break;
- }
-
- parent = parent.offsetParent;
- } while (parent && parent != element);
-
- // 对body offsetTop的修正
- if(browser.opera > 0 || (browser.isWebkit > 0 && getStyle(element, 'position') == 'absolute')){
- pos.top -= doc.body.offsetTop;
- }
-
- // 计算除了body的scroll
- parent = element.offsetParent;
- while (parent && parent != doc.body) {
- pos.left -= parent.scrollLeft;
- // see https://bugs.opera.com/show_bug.cgi?id=249965
- if (!browser.opera || parent.tagName != 'TR') {
- pos.top -= parent.scrollTop;
- }
- parent = parent.offsetParent;
- }
- }
-
- return pos;
- };
-
- /**
- * @ignore
- * @namespace baidu.event 屏蔽浏览器差异性的事件封装。
- * @property target 事件的触发元素
- * @property pageX 鼠标事件的鼠标x坐标
- * @property pageY 鼠标事件的鼠标y坐标
- * @property keyCode 键盘事件的键值
- */
- baidu.event = baidu.event || {};
-
- /**
- * 事件监听器的存储表
- * @private
- * @meta standard
- */
- baidu.event._listeners = baidu.event._listeners || [];
-
- /**
- * 为目标元素添加事件监听器
- * @name baidu.event.on
- * @function
- * @grammar baidu.event.on(element, type, listener)
- * @param {HTMLElement|string|window} element 目标元素或目标元素id
- * @param {string} type 事件类型
- * @param {Function} listener 需要添加的监听器
- * @remark
- *
- 1. 不支持跨浏览器的鼠标滚轮事件监听器添加
- 2. 改方法不为监听器灌入事件对象,以防止跨iframe事件挂载的事件对象获取失败
-
- * @shortcut on
- * @meta standard
- * @see baidu.event.un
- *
- * @returns {HTMLElement|window} 目标元素
- */
- baidu.event.on = function (element, type, listener) {
- type = type.replace(/^on/i, '');
- element = baidu.dom._g(element);
-
- var realListener = function (ev) {
- // 1. 这里不支持EventArgument, 原因是跨frame的事件挂载
- // 2. element是为了修正this
- listener.call(element, ev);
- },
- lis = baidu.event._listeners,
- filter = baidu.event._eventFilter,
- afterFilter,
- realType = type;
- type = type.toLowerCase();
- // filter过滤
- if(filter && filter[type]){
- afterFilter = filter[type](element, type, realListener);
- realType = afterFilter.type;
- realListener = afterFilter.listener;
- }
-
- // 事件监听器挂载
- if (element.addEventListener) {
- element.addEventListener(realType, realListener, false);
- } else if (element.attachEvent) {
- element.attachEvent('on' + realType, realListener);
- }
-
- // 将监听器存储到数组中
- lis[lis.length] = [element, type, listener, realListener, realType];
- return element;
- };
-
- // 声明快捷方法
- baidu.on = baidu.event.on;
-
- /**
- * 返回一个当前页面的唯一标识字符串。
- * @name baidu.lang.guid
- * @function
- * @grammar baidu.lang.guid()
- * @version 1.1.1
- * @meta standard
- *
- * @returns {String} 当前页面的唯一标识字符串
- */
-
- (function(){
- //不直接使用window,可以提高3倍左右性能
- var guid = window[baidu.guid];
-
- baidu.lang.guid = function() {
- return "TANGRAM__" + (guid._counter ++).toString(36);
- };
-
- guid._counter = guid._counter || 1;
- })();
-
- /**
- * 所有类的实例的容器
- * key为每个实例的guid
- * @meta standard
- */
-
- window[baidu.guid]._instances = window[baidu.guid]._instances || {};
-
- /**
- * 判断目标参数是否为function或Function实例
- * @name baidu.lang.isFunction
- * @function
- * @grammar baidu.lang.isFunction(source)
- * @param {Any} source 目标参数
- * @version 1.2
- * @see baidu.lang.isString,baidu.lang.isObject,baidu.lang.isNumber,baidu.lang.isArray,baidu.lang.isElement,baidu.lang.isBoolean,baidu.lang.isDate
- * @meta standard
- * @returns {boolean} 类型判断结果
- */
- baidu.lang.isFunction = function (source) {
- // chrome下,'function' == typeof /a/ 为true.
- return '[object Function]' == Object.prototype.toString.call(source);
- };
-
- /**
- *
- * @ignore
- * @class Tangram继承机制提供的一个基类,用户可以通过继承baidu.lang.Class来获取它的属性及方法。
- * @name baidu.lang.Class
- * @grammar baidu.lang.Class(guid)
- * @param {string} guid 对象的唯一标识
- * @meta standard
- * @remark baidu.lang.Class和它的子类的实例均包含一个全局唯一的标识guid。guid是在构造函数中生成的,因此,继承自baidu.lang.Class的类应该直接或者间接调用它的构造函数。
baidu.lang.Class的构造函数中产生guid的方式可以保证guid的唯一性,及每个实例都有一个全局唯一的guid。
- * @meta standard
- * @see baidu.lang.inherits,baidu.lang.Event
- */
- baidu.lang.Class = function(guid) {
- this.guid = guid || baidu.lang.guid();
- window[baidu.guid]._instances[this.guid] = this;
- };
- window[baidu.guid]._instances = window[baidu.guid]._instances || {};
-
- /**
- * 释放对象所持有的资源,主要是自定义事件。
- * @name dispose
- * @grammar obj.dispose()
- */
- baidu.lang.Class.prototype.dispose = function(){
- delete window[baidu.guid]._instances[this.guid];
-
- for(var property in this){
- if (!baidu.lang.isFunction(this[property])) {
- delete this[property];
- }
- }
- this.disposed = true;
- };
-
- /**
- * 重载了默认的toString方法,使得返回信息更加准确一些。
- * @return {string} 对象的String表示形式
- */
- baidu.lang.Class.prototype.toString = function(){
- return "[object " + (this._className || "Object" ) + "]";
- };
-
- /**
- * @ignore
- * @class 自定义的事件对象。
- * @name baidu.lang.Event
- * @grammar baidu.lang.Event(type[, target])
- * @param {string} type 事件类型名称。为了方便区分事件和一个普通的方法,事件类型名称必须以"on"(小写)开头。
- * @param {Object} [target]触发事件的对象
- * @meta standard
- * @remark 引入该模块,会自动为Class引入3个事件扩展方法:addEventListener、removeEventListener和dispatchEvent。
- * @meta standard
- * @see baidu.lang.Class
- */
- baidu.lang.Event = function (type, target) {
- this.type = type;
- this.returnValue = true;
- this.target = target || null;
- this.currentTarget = null;
- };
-
- /**
- * 注册对象的事件监听器。引入baidu.lang.Event后,Class的子类实例才会获得该方法。
- * @grammar obj.addEventListener(type, handler[, key])
- * @param {string} type 自定义事件的名称
- * @param {Function} handler 自定义事件被触发时应该调用的回调函数
- * @param {string} [key] 为事件监听函数指定的名称,可在移除时使用。如果不提供,方法会默认为它生成一个全局唯一的key。
- * @remark 事件类型区分大小写。如果自定义事件名称不是以小写"on"开头,该方法会给它加上"on"再进行判断,即"click"和"onclick"会被认为是同一种事件。
- */
- baidu.lang.Class.prototype.addEventListener = function (type, handler, key) {
- if (!baidu.lang.isFunction(handler)) {
- return;
- }
-
- !this.__listeners && (this.__listeners = {});
-
- var t = this.__listeners, id;
- if (typeof key == "string" && key) {
- if (/[^\w\-]/.test(key)) {
- throw("nonstandard key:" + key);
- } else {
- handler.hashCode = key;
- id = key;
- }
- }
- type.indexOf("on") != 0 && (type = "on" + type);
-
- typeof t[type] != "object" && (t[type] = {});
- id = id || baidu.lang.guid();
- handler.hashCode = id;
- t[type][id] = handler;
- };
-
- /**
- * 移除对象的事件监听器。引入baidu.lang.Event后,Class的子类实例才会获得该方法。
- * @grammar obj.removeEventListener(type, handler)
- * @param {string} type 事件类型
- * @param {Function|string} handler 要移除的事件监听函数或者监听函数的key
- * @remark 如果第二个参数handler没有被绑定到对应的自定义事件中,什么也不做。
- */
- baidu.lang.Class.prototype.removeEventListener = function (type, handler) {
- if (typeof handler != "undefined") {
- if ( (baidu.lang.isFunction(handler) && ! (handler = handler.hashCode))
- || (! baidu.lang.isString(handler))
- ){
- return;
- }
- }
-
- !this.__listeners && (this.__listeners = {});
-
- type.indexOf("on") != 0 && (type = "on" + type);
-
- var t = this.__listeners;
- if (!t[type]) {
- return;
- }
- if (typeof handler != "undefined") {
- t[type][handler] && delete t[type][handler];
- } else {
- for(var guid in t[type]){
- delete t[type][guid];
- }
- }
- };
-
- /**
- * 派发自定义事件,使得绑定到自定义事件上面的函数都会被执行。引入baidu.lang.Event后,Class的子类实例才会获得该方法。
- * @grammar obj.dispatchEvent(event, options)
- * @param {baidu.lang.Event|String} event Event对象,或事件名称(1.1.1起支持)
- * @param {Object} options 扩展参数,所含属性键值会扩展到Event对象上(1.2起支持)
- * @remark 处理会调用通过addEventListener绑定的自定义事件回调函数之外,还会调用直接绑定到对象上面的自定义事件。例如:
- myobj.onMyEvent = function(){}
- myobj.addEventListener("onMyEvent", function(){});
- */
- baidu.lang.Class.prototype.dispatchEvent = function (event, options) {
- if (baidu.lang.isString(event)) {
- event = new baidu.lang.Event(event);
- }
- !this.__listeners && (this.__listeners = {});
-
- // 20100603 添加本方法的第二个参数,将 options extend到event中去传递
- options = options || {};
- for (var i in options) {
- event[i] = options[i];
- }
-
- var i, t = this.__listeners, p = event.type;
- event.target = event.target || this;
- event.currentTarget = this;
-
- p.indexOf("on") != 0 && (p = "on" + p);
-
- baidu.lang.isFunction(this[p]) && this[p].apply(this, arguments);
-
- if (typeof t[p] == "object") {
- for (i in t[p]) {
- t[p][i].apply(this, arguments);
- }
- }
- return event.returnValue;
- };
-
-
- baidu.lang.inherits = function (subClass, superClass, className) {
- var key, proto,
- selfProps = subClass.prototype,
- clazz = new Function();
-
- clazz.prototype = superClass.prototype;
- proto = subClass.prototype = new clazz();
- for (key in selfProps) {
- proto[key] = selfProps[key];
- }
- subClass.prototype.constructor = subClass;
- subClass.superClass = superClass.prototype;
-
- // 类名标识,兼容Class的toString,基本没用
- if ("string" == typeof className) {
- proto._className = className;
- }
- };
- // 声明快捷方法
- baidu.inherits = baidu.lang.inherits;
- })();
-
-
- /**
-
- * 图片的路径
-
- * @private
- * @type {String}
-
- */
- var _IMAGE_PATH = 'http://api.map.baidu.com/library/TextIconOverlay/1.2/src/images/m';
-
- /**
-
- * 图片的后缀名
-
- * @private
- * @type {String}
-
- */
- var _IMAGE_EXTENSION = 'png';
-
- /**
- *@exports TextIconOverlay as BMapLib.TextIconOverlay
- */
- var TextIconOverlay =
- /**
- * TextIconOverlay
- * @class 此类表示地图上的一个覆盖物,该覆盖物由文字和图标组成,从Overlay继承。文字通常是数字(0-9)或字母(A-Z ),而文字与图标之间有一定的映射关系。
- *该覆盖物适用于以下类似的场景:需要在地图上添加一系列覆盖物,这些覆盖物之间用不同的图标和文字来区分,文字可能表示了该覆盖物的某一属性值,根据该文字和一定的映射关系,自动匹配相应颜色和大小的图标。
- *
- *@constructor
- *@param {Point} position 表示一个经纬度坐标位置。
- *@param {String} text 表示该覆盖物显示的文字信息。
- *@param {Json Object} options 可选参数,可选项包括:
- *"styles":{Array} 一组图标风格。单个图表风格包括以下几个属性:
- * url {String} 图片的url地址。(必选)
- * size {Size} 图片的大小。(必选)
- * anchor {Size} 图标定位在地图上的位置相对于图标左上角的偏移值,默认偏移值为图标的中心位置。(可选)
- * offset {Size} 图片相对于可视区域的偏移值,此功能的作用等同于CSS中的background-position属性。(可选)
- * textSize {Number} 文字的大小。(可选,默认10)
- * textColor {String} 文字的颜色。(可选,默认black)
- */
- BMapLib.TextIconOverlay = function(position, text, options){
- this._position = position;
- this._text = text;
- this._options = options || {};
- this._styles = this._options['styles'] || [];
- (!this._styles.length) && this._setupDefaultStyles();
- };
-
- T.lang.inherits(TextIconOverlay, BMapGL.Overlay, "TextIconOverlay");
-
- TextIconOverlay.prototype._setupDefaultStyles = function(){
- var sizes = [53, 56, 66, 78, 90];
- for(var i = 0, size; size = sizes[i]; i++){
- this._styles.push({
- url:_IMAGE_PATH + i + '.' + _IMAGE_EXTENSION,
- size: new BMapGL.Size(size, size)
- });
- }//for循环的简洁写法
- };
-
- /**
- *继承Overlay的initialize方法,自定义覆盖物时必须。
- *@param {Map} map BMapGL.Map的实例化对象。
- *@return {HTMLElement} 返回覆盖物对应的HTML元素。
- */
- TextIconOverlay.prototype.initialize = function(map){
- this._map = map;
-
- this._domElement = document.createElement('div');
- this._domElement.className = 'custom-image-overlay';
- // this._updateCss();
- // this._updateText();
- this._updatePosition();
-
- this._bind();
-
- this._map.getPanes().markerMouseTarget.appendChild(this._domElement);
- return this._domElement;
- };
-
- /**
- *继承Overlay的draw方法,自定义覆盖物时必须。
- *@return 无返回值。
- */
- TextIconOverlay.prototype.draw = function(){
- this._map && this._updatePosition();
- };
-
- /**
- *获取该覆盖物上的文字。
- *@return {String} 该覆盖物上的文字。
- */
- TextIconOverlay.prototype.getText = function(){
- return this._text;
- };
-
- /**
- *设置该覆盖物上的文字。
- *@param {String} text 要设置的文字,通常是字母A-Z或数字0-9。
- *@return 无返回值。
- */
- TextIconOverlay.prototype.setText = function(text, imageUrl){
- if(text && (!this._text || (this._text.toString() != text.toString()))){
- this._text = text;
- // this._updateText();
- this._updateCss(imageUrl);
- this._updatePosition();
- }
- };
-
- /**
- *获取该覆盖物的位置。
- *@return {Point} 该覆盖物的经纬度坐标。
- */
- TextIconOverlay.prototype.getPosition = function () {
- return this._position;
- };
-
- /**
- *设置该覆盖物的位置。
- *@param {Point} position 要设置的经纬度坐标。
- *@return 无返回值。
- */
- TextIconOverlay.prototype.setPosition = function (position) {
- if(position && (!this._position || !this._position.equals(position))){
- this._position = position;
- this._updatePosition();
- }
- };
-
- /**
- *由文字信息获取风格数组的对应索引值。
- *内部默认的对应函数为文字转换为数字除以10的结果,比如文字8返回索引0,文字25返回索引2.
- *如果需要自定义映射关系,请覆盖该函数。
- *@param {String} text 文字。
- *@param {Array} styles 一组图标风格。
- *@return {Number} 对应的索引值。
- */
- TextIconOverlay.prototype.getStyleByText = function(text, styles){
- var count = parseInt(text);
- var index = parseInt(count / 10);
- index = Math.max(0, index);
- index = Math.min(index, styles.length - 1);
- return styles[index];
- }
-
- /**
- *更新相应的CSS。
- *@return 无返回值。
- */
- TextIconOverlay.prototype._updateCss = function(imageUrl){
- var style = this.getStyleByText(this._text, this._styles);
- var newStyle = {
- url: imageUrl,
- size: { width: 86, height: 86 }
- }
- if (imageUrl) {
- style = Object.assign(style, { url: imageUrl, size: { width: 86, height: 86 } })
- }
-
- const customImageNumber = `${this._text < 1000 ? this._text : '1k+'}`;
- this._domElement.style.cssText = this.buildImageCssText(newStyle);
- const imageElement = `
`
- const htmlString = `
-
- ${this._text > 1 ? customImageNumber : ''}
- ${imageUrl ? imageElement : '
'}
-
- `
- const labelDocument = new DOMParser().parseFromString(htmlString, 'text/html');
- const label = labelDocument.body.firstElementChild;
- this._domElement.append(label);
- };
-
- TextIconOverlay.prototype.buildImageCssText = function(style) {
- //根据style来确定一些默认值
- var size = style['size'];
- var anchor = style['anchor'];
- var textColor = style['textColor'] || 'black';
- var textSize = style['textSize'] || 10;
-
- var cssText = [];
-
- cssText.push('height:' + size.height + 'px; line-height:' + size.height + 'px;');
- cssText.push('width:' + size.width + 'px; text-align:center;');
-
- cssText.push('cursor:pointer; color:' + textColor + '; position:absolute; font-size:' +
- textSize + 'px; font-family:Arial,sans-serif; font-weight:bold');
- return cssText.join('');
- };
-
- /**
- *更新覆盖物的显示文字。
- *@return 无返回值。
- */
- TextIconOverlay.prototype._updateText = function(){
- if (this._domElement) {
- this._domElement.innerHTML = this._text;
- }
- };
-
- /**
- *调整覆盖物在地图上的位置更新覆盖物的显示文字。
- *@return 无返回值。
- */
- TextIconOverlay.prototype._updatePosition = function(){
- if (this._domElement && this._position) {
- var style = this._domElement.style;
- var pixelPosition= this._map.pointToOverlayPixel(this._position);
- pixelPosition.x -= Math.ceil(parseInt(style.width) / 2);
- pixelPosition.y -= Math.ceil(parseInt(style.height) + 8);
- style.left = pixelPosition.x + "px";
- style.top = pixelPosition.y + "px";
- }
- };
-
- /**
- * 为该覆盖物的HTML元素构建CSS
- * @param {IconStyle} 一个图标的风格。
- * @return {String} 构建完成的CSSTEXT。
- */
- TextIconOverlay.prototype._buildCssText = function(style) {
- //根据style来确定一些默认值
- var url = style['url'];
- var size = style['size'];
- var anchor = style['anchor'];
- var offset = style['offset'];
- var textColor = style['textColor'] || 'black';
- var textSize = style['textSize'] || 10;
-
- var cssText = [];
- if (T.browser["ie"] < 7) {
- cssText.push('filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(' +
- 'sizingMethod=scale,src="' + url + '");');
- } else {
- cssText.push('background-image:url(' + url + ');');
- var backgroundPosition = '0 0';
- (offset instanceof BMapGL.Size) && (backgroundPosition = offset.width + 'px' + ' ' + offset.height + 'px');
- cssText.push('background-position:' + backgroundPosition + ';');
- }
-
- if (size instanceof BMapGL.Size){
- if (anchor instanceof BMapGL.Size) {
- if (anchor.height > 0 && anchor.height < size.height) {
- cssText.push('height:' + (size.height - anchor.height) + 'px; padding-top:' + anchor.height + 'px;');
- }
- if(anchor.width > 0 && anchor.width < size.width){
- cssText.push('width:' + (size.width - anchor.width) + 'px; padding-left:' + anchor.width + 'px;');
- }
- } else {
- cssText.push('height:' + size.height + 'px; line-height:' + size.height + 'px;');
- cssText.push('width:' + size.width + 'px; text-align:center;');
- }
- }
-
- cssText.push('cursor:pointer; color:' + textColor + '; position:absolute; font-size:' +
- textSize + 'px; font-family:Arial,sans-serif; font-weight:bold');
- return cssText.join('');
- };
-
-
- /**
-
- * 当鼠标点击该覆盖物时会触发该事件
-
- * @name TextIconOverlay#click
-
- * @event
-
- * @param {Event Object} e 回调函数会返回event参数,包括以下返回值:
-
- *
"type : {String} 事件类型
-
- *
"target:{BMapLib.TextIconOverlay} 事件目标
-
- *
-
- */
-
- /**
-
- * 当鼠标进入该覆盖物区域时会触发该事件
-
- * @name TextIconOverlay#mouseover
-
- * @event
- * @param {Event Object} e 回调函数会返回event参数,包括以下返回值:
-
- *
"type : {String} 事件类型
-
- *
"target:{BMapLib.TextIconOverlay} 事件目标
-
- *
"point : {BMapGL.Point} 最新添加上的节点BMap.Point对象
-
- *
"pixel:{BMapGL.pixel} 最新添加上的节点BMap.Pixel对象
-
- *
-
- * @example 参考示例:
-
- * myTextIconOverlay.addEventListener("mouseover", function(e) { alert(e.point); });
-
- */
-
- /**
-
- * 当鼠标离开该覆盖物区域时会触发该事件
-
- * @name TextIconOverlay#mouseout
-
- * @event
-
- * @param {Event Object} e 回调函数会返回event参数,包括以下返回值:
-
- *
"type : {String} 事件类型
-
- *
"target:{BMapLib.TextIconOverlay} 事件目标
-
- *
"point : {BMapGL.Point} 最新添加上的节点BMap.Point对象
-
- *
"pixel:{BMapGL.pixel} 最新添加上的节点BMap.Pixel对象
-
- *
-
- * @example 参考示例:
-
- * myTextIconOverlay.addEventListener("mouseout", function(e) { alert(e.point); });
-
- */
-
-
- /**
- * 为该覆盖物绑定一系列事件
- * 当前支持click mouseover mouseout
- * @return 无返回值。
- */
- TextIconOverlay.prototype._bind = function(){
- if (!this._domElement){
- return;
- }
-
- var me = this;
- var map = this._map;
-
- var BaseEvent = T.lang.Event;
- function eventExtend(e, be){
- var elem = e.srcElement || e.target;
- var x = e.clientX || e.pageX;
- var y = e.clientY || e.pageY;
- if (e && be && x && y && elem){
- var offset = T.dom.getPosition(map.getContainer());
- be.pixel = new BMapGL.Pixel(x - offset.left, y - offset.top);
- be.point = map.pixelToPoint(be.pixel);
- }
- return be;
- }//给事件参数增加pixel和point两个值
-
- T.event.on(this._domElement,"mouseover", function(e){
- me.dispatchEvent(eventExtend(e, new BaseEvent("onmouseover")));
- });
- T.event.on(this._domElement,"mouseout", function(e){
- me.dispatchEvent(eventExtend(e, new BaseEvent("onmouseout")));
- });
- T.event.on(this._domElement,"click", function(e){
- me.dispatchEvent(eventExtend(e, new BaseEvent("onclick")));
- });
- };
-
-})();