tg-map-core 4.1.3 → 4.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/tg-map-core.cjs +4041 -9
- package/dist/tg-map-core.mjs +4040 -5
- package/package.json +6 -4
package/dist/tg-map-core.cjs
CHANGED
|
@@ -4,9 +4,6 @@ var maptalks = require('maptalks');
|
|
|
4
4
|
var tgCommons = require('tg-commons');
|
|
5
5
|
var coordtransform = require('coordtransform');
|
|
6
6
|
var deepmerge = require('deepmerge');
|
|
7
|
-
var BHeatMapOverlay = require('bmaplib.heatmap');
|
|
8
|
-
var GMarkerClusterer = require('@google/markerclusterer');
|
|
9
|
-
var BMarkerClusterer = require('bmaplib.markerclusterer');
|
|
10
7
|
var isPlainObject = require('is-plain-object');
|
|
11
8
|
require('maptalks/dist/maptalks.css');
|
|
12
9
|
|
|
@@ -33,9 +30,6 @@ function _interopNamespace(e) {
|
|
|
33
30
|
var maptalks__namespace = /*#__PURE__*/_interopNamespace(maptalks);
|
|
34
31
|
var coordtransform__default = /*#__PURE__*/_interopDefault(coordtransform);
|
|
35
32
|
var deepmerge__default = /*#__PURE__*/_interopDefault(deepmerge);
|
|
36
|
-
var BHeatMapOverlay__default = /*#__PURE__*/_interopDefault(BHeatMapOverlay);
|
|
37
|
-
var GMarkerClusterer__default = /*#__PURE__*/_interopDefault(GMarkerClusterer);
|
|
38
|
-
var BMarkerClusterer__default = /*#__PURE__*/_interopDefault(BMarkerClusterer);
|
|
39
33
|
var isPlainObject__default = /*#__PURE__*/_interopDefault(isPlainObject);
|
|
40
34
|
|
|
41
35
|
function bindEvents(listeners, target, excludes) {
|
|
@@ -1568,6 +1562,1023 @@ class BaiduAutocomplete extends Autocomplete {
|
|
|
1568
1562
|
}
|
|
1569
1563
|
}
|
|
1570
1564
|
|
|
1565
|
+
function getDefaultExportFromCjs (x) {
|
|
1566
|
+
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
var bmaplib_heatmap$1 = {exports: {}};
|
|
1570
|
+
|
|
1571
|
+
/*
|
|
1572
|
+
* heatmap.js v2.0.0 | JavaScript Heatmap Library
|
|
1573
|
+
*
|
|
1574
|
+
* Copyright 2008-2014 Patrick Wied <heatmapjs@patrick-wied.at> - All rights reserved.
|
|
1575
|
+
* Dual licensed under MIT and Beerware license
|
|
1576
|
+
*
|
|
1577
|
+
* :: 2014-10-31 21:16
|
|
1578
|
+
*/
|
|
1579
|
+
var bmaplib_heatmap = bmaplib_heatmap$1.exports;
|
|
1580
|
+
|
|
1581
|
+
var hasRequiredBmaplib_heatmap;
|
|
1582
|
+
|
|
1583
|
+
function requireBmaplib_heatmap () {
|
|
1584
|
+
if (hasRequiredBmaplib_heatmap) return bmaplib_heatmap$1.exports;
|
|
1585
|
+
hasRequiredBmaplib_heatmap = 1;
|
|
1586
|
+
(function (module, exports) {
|
|
1587
|
+
(function (root, factory) {
|
|
1588
|
+
{
|
|
1589
|
+
module.exports = factory();
|
|
1590
|
+
}
|
|
1591
|
+
})(bmaplib_heatmap, function () {
|
|
1592
|
+
const heatmapFactory = (function () {
|
|
1593
|
+
// Heatmap Config stores default values and will be merged with instance config
|
|
1594
|
+
var HeatmapConfig = {
|
|
1595
|
+
defaultRadius: 40,
|
|
1596
|
+
defaultRenderer: 'canvas2d',
|
|
1597
|
+
defaultGradient: { 0.45: "rgb(0,0,255)", 0.55: "rgb(0,255,255)", 0.65: "rgb(0,255,0)", 0.95: "yellow", 1.0: "rgb(255,0,0)" },
|
|
1598
|
+
defaultMaxOpacity: 1,
|
|
1599
|
+
defaultMinOpacity: 0,
|
|
1600
|
+
defaultBlur: .85,
|
|
1601
|
+
defaultXField: 'x',
|
|
1602
|
+
defaultYField: 'y',
|
|
1603
|
+
defaultValueField: 'value',
|
|
1604
|
+
plugins: {}
|
|
1605
|
+
};
|
|
1606
|
+
var Store = (function StoreClosure() {
|
|
1607
|
+
|
|
1608
|
+
var Store = function Store(config) {
|
|
1609
|
+
this._coordinator = {};
|
|
1610
|
+
this._data = [];
|
|
1611
|
+
this._radi = [];
|
|
1612
|
+
this._min = 0;
|
|
1613
|
+
this._max = 1;
|
|
1614
|
+
this._xField = config['xField'] || config.defaultXField;
|
|
1615
|
+
this._yField = config['yField'] || config.defaultYField;
|
|
1616
|
+
this._valueField = config['valueField'] || config.defaultValueField;
|
|
1617
|
+
|
|
1618
|
+
if (config["radius"]) {
|
|
1619
|
+
this._cfgRadius = config["radius"];
|
|
1620
|
+
}
|
|
1621
|
+
};
|
|
1622
|
+
|
|
1623
|
+
var defaultRadius = HeatmapConfig.defaultRadius;
|
|
1624
|
+
|
|
1625
|
+
Store.prototype = {
|
|
1626
|
+
// when forceRender = false -> called from setData, omits renderall event
|
|
1627
|
+
_organiseData: function (dataPoint, forceRender) {
|
|
1628
|
+
var x = dataPoint[this._xField];
|
|
1629
|
+
var y = dataPoint[this._yField];
|
|
1630
|
+
var radi = this._radi;
|
|
1631
|
+
var store = this._data;
|
|
1632
|
+
var max = this._max;
|
|
1633
|
+
var min = this._min;
|
|
1634
|
+
var value = dataPoint[this._valueField] || 1;
|
|
1635
|
+
var radius = dataPoint.radius || this._cfgRadius || defaultRadius;
|
|
1636
|
+
|
|
1637
|
+
if (!store[x]) {
|
|
1638
|
+
store[x] = [];
|
|
1639
|
+
radi[x] = [];
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
if (!store[x][y]) {
|
|
1643
|
+
store[x][y] = value;
|
|
1644
|
+
radi[x][y] = radius;
|
|
1645
|
+
} else {
|
|
1646
|
+
store[x][y] += value;
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
if (store[x][y] > max) {
|
|
1650
|
+
if (!forceRender) {
|
|
1651
|
+
this._max = store[x][y];
|
|
1652
|
+
} else {
|
|
1653
|
+
this.setDataMax(store[x][y]);
|
|
1654
|
+
}
|
|
1655
|
+
return false;
|
|
1656
|
+
} else {
|
|
1657
|
+
return {
|
|
1658
|
+
x: x,
|
|
1659
|
+
y: y,
|
|
1660
|
+
value: value,
|
|
1661
|
+
radius: radius,
|
|
1662
|
+
min: min,
|
|
1663
|
+
max: max
|
|
1664
|
+
};
|
|
1665
|
+
}
|
|
1666
|
+
},
|
|
1667
|
+
_unOrganizeData: function () {
|
|
1668
|
+
var unorganizedData = [];
|
|
1669
|
+
var data = this._data;
|
|
1670
|
+
var radi = this._radi;
|
|
1671
|
+
|
|
1672
|
+
for (var x in data) {
|
|
1673
|
+
for (var y in data[x]) {
|
|
1674
|
+
|
|
1675
|
+
unorganizedData.push({
|
|
1676
|
+
x: x,
|
|
1677
|
+
y: y,
|
|
1678
|
+
radius: radi[x][y],
|
|
1679
|
+
value: data[x][y]
|
|
1680
|
+
});
|
|
1681
|
+
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
return {
|
|
1685
|
+
min: this._min,
|
|
1686
|
+
max: this._max,
|
|
1687
|
+
data: unorganizedData
|
|
1688
|
+
};
|
|
1689
|
+
},
|
|
1690
|
+
_onExtremaChange: function () {
|
|
1691
|
+
this._coordinator.emit('extremachange', {
|
|
1692
|
+
min: this._min,
|
|
1693
|
+
max: this._max
|
|
1694
|
+
});
|
|
1695
|
+
},
|
|
1696
|
+
addData: function () {
|
|
1697
|
+
if (arguments[0].length > 0) {
|
|
1698
|
+
var dataArr = arguments[0];
|
|
1699
|
+
var dataLen = dataArr.length;
|
|
1700
|
+
while (dataLen--) {
|
|
1701
|
+
this.addData.call(this, dataArr[dataLen]);
|
|
1702
|
+
}
|
|
1703
|
+
} else {
|
|
1704
|
+
// add to store
|
|
1705
|
+
var organisedEntry = this._organiseData(arguments[0], true);
|
|
1706
|
+
if (organisedEntry) {
|
|
1707
|
+
this._coordinator.emit('renderpartial', {
|
|
1708
|
+
min: this._min,
|
|
1709
|
+
max: this._max,
|
|
1710
|
+
data: [organisedEntry]
|
|
1711
|
+
});
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
return this;
|
|
1715
|
+
},
|
|
1716
|
+
setData: function (data) {
|
|
1717
|
+
var dataPoints = data.data;
|
|
1718
|
+
var pointsLen = dataPoints.length;
|
|
1719
|
+
|
|
1720
|
+
|
|
1721
|
+
// reset data arrays
|
|
1722
|
+
this._data = [];
|
|
1723
|
+
this._radi = [];
|
|
1724
|
+
|
|
1725
|
+
for (var i = 0; i < pointsLen; i++) {
|
|
1726
|
+
this._organiseData(dataPoints[i], false);
|
|
1727
|
+
}
|
|
1728
|
+
this._max = data.max;
|
|
1729
|
+
this._min = data.min || 0;
|
|
1730
|
+
|
|
1731
|
+
this._onExtremaChange();
|
|
1732
|
+
this._coordinator.emit('renderall', this._getInternalData());
|
|
1733
|
+
return this;
|
|
1734
|
+
},
|
|
1735
|
+
removeData: function () {
|
|
1736
|
+
// TODO: implement
|
|
1737
|
+
},
|
|
1738
|
+
setDataMax: function (max) {
|
|
1739
|
+
this._max = max;
|
|
1740
|
+
this._onExtremaChange();
|
|
1741
|
+
this._coordinator.emit('renderall', this._getInternalData());
|
|
1742
|
+
return this;
|
|
1743
|
+
},
|
|
1744
|
+
setDataMin: function (min) {
|
|
1745
|
+
this._min = min;
|
|
1746
|
+
this._onExtremaChange();
|
|
1747
|
+
this._coordinator.emit('renderall', this._getInternalData());
|
|
1748
|
+
return this;
|
|
1749
|
+
},
|
|
1750
|
+
setCoordinator: function (coordinator) {
|
|
1751
|
+
this._coordinator = coordinator;
|
|
1752
|
+
},
|
|
1753
|
+
_getInternalData: function () {
|
|
1754
|
+
return {
|
|
1755
|
+
max: this._max,
|
|
1756
|
+
min: this._min,
|
|
1757
|
+
data: this._data,
|
|
1758
|
+
radi: this._radi
|
|
1759
|
+
};
|
|
1760
|
+
},
|
|
1761
|
+
getData: function () {
|
|
1762
|
+
return this._unOrganizeData();
|
|
1763
|
+
}/*,
|
|
1764
|
+
|
|
1765
|
+
// TODO: rethink.
|
|
1766
|
+
getValueAt: function (point) {
|
|
1767
|
+
var value;
|
|
1768
|
+
var radius = 100;
|
|
1769
|
+
var x = point.x;
|
|
1770
|
+
var y = point.y;
|
|
1771
|
+
var data = this._data;
|
|
1772
|
+
|
|
1773
|
+
if (data[x] && data[x][y]) {
|
|
1774
|
+
return data[x][y];
|
|
1775
|
+
} else {
|
|
1776
|
+
var values = [];
|
|
1777
|
+
// radial search for datapoints based on default radius
|
|
1778
|
+
for (var distance = 1; distance < radius; distance++) {
|
|
1779
|
+
var neighbors = distance * 2 + 1;
|
|
1780
|
+
var startX = x - distance;
|
|
1781
|
+
var startY = y - distance;
|
|
1782
|
+
|
|
1783
|
+
for (var i = 0; i < neighbors; i++) {
|
|
1784
|
+
for (var o = 0; o < neighbors; o++) {
|
|
1785
|
+
if ((i == 0 || i == neighbors - 1) || (o == 0 || o == neighbors - 1)) {
|
|
1786
|
+
if (data[startY + i] && data[startY + i][startX + o]) {
|
|
1787
|
+
values.push(data[startY + i][startX + o]);
|
|
1788
|
+
}
|
|
1789
|
+
} else {
|
|
1790
|
+
continue;
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
if (values.length > 0) {
|
|
1796
|
+
return Math.max.apply(Math, values);
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
return false;
|
|
1800
|
+
}*/
|
|
1801
|
+
};
|
|
1802
|
+
|
|
1803
|
+
|
|
1804
|
+
return Store;
|
|
1805
|
+
})();
|
|
1806
|
+
|
|
1807
|
+
var Canvas2dRenderer = (function Canvas2dRendererClosure() {
|
|
1808
|
+
|
|
1809
|
+
var _getColorPalette = function (config) {
|
|
1810
|
+
var gradientConfig = config.gradient || config.defaultGradient;
|
|
1811
|
+
var paletteCanvas = document.createElement('canvas');
|
|
1812
|
+
var paletteCtx = paletteCanvas.getContext('2d');
|
|
1813
|
+
|
|
1814
|
+
paletteCanvas.width = 256;
|
|
1815
|
+
paletteCanvas.height = 1;
|
|
1816
|
+
|
|
1817
|
+
var gradient = paletteCtx.createLinearGradient(0, 0, 256, 1);
|
|
1818
|
+
for (var key in gradientConfig) {
|
|
1819
|
+
gradient.addColorStop(key, gradientConfig[key]);
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
paletteCtx.fillStyle = gradient;
|
|
1823
|
+
paletteCtx.fillRect(0, 0, 256, 1);
|
|
1824
|
+
|
|
1825
|
+
return paletteCtx.getImageData(0, 0, 256, 1).data;
|
|
1826
|
+
};
|
|
1827
|
+
|
|
1828
|
+
var _getPointTemplate = function (radius, blurFactor) {
|
|
1829
|
+
var tplCanvas = document.createElement('canvas');
|
|
1830
|
+
var tplCtx = tplCanvas.getContext('2d');
|
|
1831
|
+
var x = radius;
|
|
1832
|
+
var y = radius;
|
|
1833
|
+
tplCanvas.width = tplCanvas.height = radius * 2;
|
|
1834
|
+
|
|
1835
|
+
if (blurFactor == 1) {
|
|
1836
|
+
tplCtx.beginPath();
|
|
1837
|
+
tplCtx.arc(x, y, radius, 0, 2 * Math.PI, false);
|
|
1838
|
+
tplCtx.fillStyle = 'rgba(0,0,0,1)';
|
|
1839
|
+
tplCtx.fill();
|
|
1840
|
+
} else {
|
|
1841
|
+
var gradient = tplCtx.createRadialGradient(x, y, radius * blurFactor, x, y, radius);
|
|
1842
|
+
gradient.addColorStop(0, 'rgba(0,0,0,1)');
|
|
1843
|
+
gradient.addColorStop(1, 'rgba(0,0,0,0)');
|
|
1844
|
+
tplCtx.fillStyle = gradient;
|
|
1845
|
+
tplCtx.fillRect(0, 0, 2 * radius, 2 * radius);
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
|
|
1849
|
+
|
|
1850
|
+
return tplCanvas;
|
|
1851
|
+
};
|
|
1852
|
+
|
|
1853
|
+
var _prepareData = function (data) {
|
|
1854
|
+
var renderData = [];
|
|
1855
|
+
var min = data.min;
|
|
1856
|
+
var max = data.max;
|
|
1857
|
+
var radi = data.radi;
|
|
1858
|
+
var data = data.data;
|
|
1859
|
+
|
|
1860
|
+
var xValues = Object.keys(data);
|
|
1861
|
+
var xValuesLen = xValues.length;
|
|
1862
|
+
|
|
1863
|
+
while (xValuesLen--) {
|
|
1864
|
+
var xValue = xValues[xValuesLen];
|
|
1865
|
+
var yValues = Object.keys(data[xValue]);
|
|
1866
|
+
var yValuesLen = yValues.length;
|
|
1867
|
+
while (yValuesLen--) {
|
|
1868
|
+
var yValue = yValues[yValuesLen];
|
|
1869
|
+
var value = data[xValue][yValue];
|
|
1870
|
+
var radius = radi[xValue][yValue];
|
|
1871
|
+
renderData.push({
|
|
1872
|
+
x: xValue,
|
|
1873
|
+
y: yValue,
|
|
1874
|
+
value: value,
|
|
1875
|
+
radius: radius
|
|
1876
|
+
});
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
return {
|
|
1881
|
+
min: min,
|
|
1882
|
+
max: max,
|
|
1883
|
+
data: renderData
|
|
1884
|
+
};
|
|
1885
|
+
};
|
|
1886
|
+
|
|
1887
|
+
|
|
1888
|
+
function Canvas2dRenderer(config) {
|
|
1889
|
+
var container = config.element;
|
|
1890
|
+
var shadowCanvas = this.shadowCanvas = document.createElement('canvas');
|
|
1891
|
+
var canvas = this.canvas = config.canvas || document.createElement('canvas');
|
|
1892
|
+
this._renderBoundaries = [10000, 10000, 0, 0];
|
|
1893
|
+
|
|
1894
|
+
var computed = getComputedStyle(config.element) || {};
|
|
1895
|
+
|
|
1896
|
+
canvas.className = 'heatmap-canvas';
|
|
1897
|
+
|
|
1898
|
+
this._width = canvas.width = shadowCanvas.width = +(computed.width.replace(/px/, ''));
|
|
1899
|
+
this._height = canvas.height = shadowCanvas.height = +(computed.height.replace(/px/, ''));
|
|
1900
|
+
|
|
1901
|
+
this.shadowCtx = shadowCanvas.getContext('2d');
|
|
1902
|
+
this.ctx = canvas.getContext('2d');
|
|
1903
|
+
|
|
1904
|
+
// @TODO:
|
|
1905
|
+
// conditional wrapper
|
|
1906
|
+
|
|
1907
|
+
canvas.style.cssText = shadowCanvas.style.cssText = 'position:absolute;left:0;top:0;';
|
|
1908
|
+
|
|
1909
|
+
container.style.position = 'relative';
|
|
1910
|
+
container.appendChild(canvas);
|
|
1911
|
+
|
|
1912
|
+
this._palette = _getColorPalette(config);
|
|
1913
|
+
this._templates = {};
|
|
1914
|
+
|
|
1915
|
+
this._setStyles(config);
|
|
1916
|
+
}
|
|
1917
|
+
Canvas2dRenderer.prototype = {
|
|
1918
|
+
renderPartial: function (data) {
|
|
1919
|
+
this._drawAlpha(data);
|
|
1920
|
+
this._colorize();
|
|
1921
|
+
},
|
|
1922
|
+
renderAll: function (data) {
|
|
1923
|
+
// reset render boundaries
|
|
1924
|
+
this._clear();
|
|
1925
|
+
this._drawAlpha(_prepareData(data));
|
|
1926
|
+
this._colorize();
|
|
1927
|
+
},
|
|
1928
|
+
_updateGradient: function (config) {
|
|
1929
|
+
this._palette = _getColorPalette(config);
|
|
1930
|
+
},
|
|
1931
|
+
updateConfig: function (config, oldConfig) {
|
|
1932
|
+
if (config.gradient !== oldConfig.gradient) {
|
|
1933
|
+
this._updateGradient(config);
|
|
1934
|
+
}
|
|
1935
|
+
this._setStyles(config);
|
|
1936
|
+
},
|
|
1937
|
+
setDimensions: function (width, height) {
|
|
1938
|
+
this._width = width;
|
|
1939
|
+
this._height = height;
|
|
1940
|
+
this.canvas.width = this.shadowCanvas.width = width;
|
|
1941
|
+
this.canvas.height = this.shadowCanvas.height = height;
|
|
1942
|
+
},
|
|
1943
|
+
_clear: function () {
|
|
1944
|
+
this.shadowCtx.clearRect(0, 0, this._width, this._height);
|
|
1945
|
+
this.ctx.clearRect(0, 0, this._width, this._height);
|
|
1946
|
+
},
|
|
1947
|
+
_setStyles: function (config) {
|
|
1948
|
+
this._blur = (config.blur == 0) ? 0 : (config.blur || config.defaultBlur);
|
|
1949
|
+
|
|
1950
|
+
if (config.backgroundColor) {
|
|
1951
|
+
this.canvas.style.backgroundColor = config.backgroundColor;
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
this._opacity = (config.opacity || 0) * 255;
|
|
1955
|
+
this._maxOpacity = (config.maxOpacity || config.defaultMaxOpacity) * 255;
|
|
1956
|
+
this._minOpacity = (config.minOpacity || config.defaultMinOpacity) * 255;
|
|
1957
|
+
this._useGradientOpacity = !!config.useGradientOpacity;
|
|
1958
|
+
},
|
|
1959
|
+
_drawAlpha: function (data) {
|
|
1960
|
+
var min = this._min = data.min;
|
|
1961
|
+
var max = this._max = data.max;
|
|
1962
|
+
var data = data.data || [];
|
|
1963
|
+
var dataLen = data.length;
|
|
1964
|
+
// on a point basis?
|
|
1965
|
+
var blur = 1 - this._blur;
|
|
1966
|
+
|
|
1967
|
+
while (dataLen--) {
|
|
1968
|
+
|
|
1969
|
+
var point = data[dataLen];
|
|
1970
|
+
|
|
1971
|
+
var x = point.x;
|
|
1972
|
+
var y = point.y;
|
|
1973
|
+
var radius = point.radius;
|
|
1974
|
+
// if value is bigger than max
|
|
1975
|
+
// use max as value
|
|
1976
|
+
var value = Math.min(point.value, max);
|
|
1977
|
+
var rectX = x - radius;
|
|
1978
|
+
var rectY = y - radius;
|
|
1979
|
+
var shadowCtx = this.shadowCtx;
|
|
1980
|
+
|
|
1981
|
+
|
|
1982
|
+
|
|
1983
|
+
|
|
1984
|
+
var tpl;
|
|
1985
|
+
if (!this._templates[radius]) {
|
|
1986
|
+
this._templates[radius] = tpl = _getPointTemplate(radius, blur);
|
|
1987
|
+
} else {
|
|
1988
|
+
tpl = this._templates[radius];
|
|
1989
|
+
}
|
|
1990
|
+
// value from minimum / value range
|
|
1991
|
+
// => [0, 1]
|
|
1992
|
+
shadowCtx.globalAlpha = (value - min) / (max - min);
|
|
1993
|
+
|
|
1994
|
+
shadowCtx.drawImage(tpl, rectX, rectY);
|
|
1995
|
+
|
|
1996
|
+
// update renderBoundaries
|
|
1997
|
+
if (rectX < this._renderBoundaries[0]) {
|
|
1998
|
+
this._renderBoundaries[0] = rectX;
|
|
1999
|
+
}
|
|
2000
|
+
if (rectY < this._renderBoundaries[1]) {
|
|
2001
|
+
this._renderBoundaries[1] = rectY;
|
|
2002
|
+
}
|
|
2003
|
+
if (rectX + 2 * radius > this._renderBoundaries[2]) {
|
|
2004
|
+
this._renderBoundaries[2] = rectX + 2 * radius;
|
|
2005
|
+
}
|
|
2006
|
+
if (rectY + 2 * radius > this._renderBoundaries[3]) {
|
|
2007
|
+
this._renderBoundaries[3] = rectY + 2 * radius;
|
|
2008
|
+
}
|
|
2009
|
+
|
|
2010
|
+
}
|
|
2011
|
+
},
|
|
2012
|
+
_colorize: function () {
|
|
2013
|
+
var x = this._renderBoundaries[0];
|
|
2014
|
+
var y = this._renderBoundaries[1];
|
|
2015
|
+
var width = this._renderBoundaries[2] - x;
|
|
2016
|
+
var height = this._renderBoundaries[3] - y;
|
|
2017
|
+
var maxWidth = this._width;
|
|
2018
|
+
var maxHeight = this._height;
|
|
2019
|
+
var opacity = this._opacity;
|
|
2020
|
+
var maxOpacity = this._maxOpacity;
|
|
2021
|
+
var minOpacity = this._minOpacity;
|
|
2022
|
+
var useGradientOpacity = this._useGradientOpacity;
|
|
2023
|
+
|
|
2024
|
+
if (x < 0) {
|
|
2025
|
+
x = 0;
|
|
2026
|
+
}
|
|
2027
|
+
if (y < 0) {
|
|
2028
|
+
y = 0;
|
|
2029
|
+
}
|
|
2030
|
+
if (x + width > maxWidth) {
|
|
2031
|
+
width = maxWidth - x;
|
|
2032
|
+
}
|
|
2033
|
+
if (y + height > maxHeight) {
|
|
2034
|
+
height = maxHeight - y;
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2037
|
+
var img = this.shadowCtx.getImageData(x, y, width, height);
|
|
2038
|
+
var imgData = img.data;
|
|
2039
|
+
var len = imgData.length;
|
|
2040
|
+
var palette = this._palette;
|
|
2041
|
+
|
|
2042
|
+
|
|
2043
|
+
for (var i = 3; i < len; i += 4) {
|
|
2044
|
+
var alpha = imgData[i];
|
|
2045
|
+
var offset = alpha * 4;
|
|
2046
|
+
|
|
2047
|
+
|
|
2048
|
+
if (!offset) {
|
|
2049
|
+
continue;
|
|
2050
|
+
}
|
|
2051
|
+
|
|
2052
|
+
var finalAlpha;
|
|
2053
|
+
if (opacity > 0) {
|
|
2054
|
+
finalAlpha = opacity;
|
|
2055
|
+
} else {
|
|
2056
|
+
if (alpha < maxOpacity) {
|
|
2057
|
+
if (alpha < minOpacity) {
|
|
2058
|
+
finalAlpha = minOpacity;
|
|
2059
|
+
} else {
|
|
2060
|
+
finalAlpha = alpha;
|
|
2061
|
+
}
|
|
2062
|
+
} else {
|
|
2063
|
+
finalAlpha = maxOpacity;
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
imgData[i - 3] = palette[offset];
|
|
2068
|
+
imgData[i - 2] = palette[offset + 1];
|
|
2069
|
+
imgData[i - 1] = palette[offset + 2];
|
|
2070
|
+
imgData[i] = useGradientOpacity ? palette[offset + 3] : finalAlpha;
|
|
2071
|
+
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
this.ctx.putImageData(img, x, y);
|
|
2075
|
+
|
|
2076
|
+
this._renderBoundaries = [1000, 1000, 0, 0];
|
|
2077
|
+
|
|
2078
|
+
},
|
|
2079
|
+
getValueAt: function (point) {
|
|
2080
|
+
var value;
|
|
2081
|
+
var shadowCtx = this.shadowCtx;
|
|
2082
|
+
var img = shadowCtx.getImageData(point.x, point.y, 1, 1);
|
|
2083
|
+
var data = img.data[3];
|
|
2084
|
+
var max = this._max;
|
|
2085
|
+
var min = this._min;
|
|
2086
|
+
|
|
2087
|
+
value = (Math.abs(max - min) * (data / 255)) >> 0;
|
|
2088
|
+
|
|
2089
|
+
return value;
|
|
2090
|
+
},
|
|
2091
|
+
getDataURL: function () {
|
|
2092
|
+
return this.canvas.toDataURL();
|
|
2093
|
+
}
|
|
2094
|
+
};
|
|
2095
|
+
|
|
2096
|
+
|
|
2097
|
+
return Canvas2dRenderer;
|
|
2098
|
+
})();
|
|
2099
|
+
|
|
2100
|
+
var Renderer = (function RendererClosure() {
|
|
2101
|
+
|
|
2102
|
+
var rendererFn = false;
|
|
2103
|
+
|
|
2104
|
+
if (HeatmapConfig['defaultRenderer'] === 'canvas2d') {
|
|
2105
|
+
rendererFn = Canvas2dRenderer;
|
|
2106
|
+
}
|
|
2107
|
+
|
|
2108
|
+
return rendererFn;
|
|
2109
|
+
})();
|
|
2110
|
+
|
|
2111
|
+
|
|
2112
|
+
var Util = {
|
|
2113
|
+
merge: function () {
|
|
2114
|
+
var merged = {};
|
|
2115
|
+
var argsLen = arguments.length;
|
|
2116
|
+
for (var i = 0; i < argsLen; i++) {
|
|
2117
|
+
var obj = arguments[i];
|
|
2118
|
+
for (var key in obj) {
|
|
2119
|
+
merged[key] = obj[key];
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
return merged;
|
|
2123
|
+
}
|
|
2124
|
+
};
|
|
2125
|
+
// Heatmap Constructor
|
|
2126
|
+
var Heatmap = (function HeatmapClosure() {
|
|
2127
|
+
|
|
2128
|
+
var Coordinator = (function CoordinatorClosure() {
|
|
2129
|
+
|
|
2130
|
+
function Coordinator() {
|
|
2131
|
+
this.cStore = {};
|
|
2132
|
+
}
|
|
2133
|
+
Coordinator.prototype = {
|
|
2134
|
+
on: function (evtName, callback, scope) {
|
|
2135
|
+
var cStore = this.cStore;
|
|
2136
|
+
|
|
2137
|
+
if (!cStore[evtName]) {
|
|
2138
|
+
cStore[evtName] = [];
|
|
2139
|
+
}
|
|
2140
|
+
cStore[evtName].push((function (data) {
|
|
2141
|
+
return callback.call(scope, data);
|
|
2142
|
+
}));
|
|
2143
|
+
},
|
|
2144
|
+
emit: function (evtName, data) {
|
|
2145
|
+
var cStore = this.cStore;
|
|
2146
|
+
if (cStore[evtName]) {
|
|
2147
|
+
var len = cStore[evtName].length;
|
|
2148
|
+
for (var i = 0; i < len; i++) {
|
|
2149
|
+
var callback = cStore[evtName][i];
|
|
2150
|
+
callback(data);
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
};
|
|
2155
|
+
|
|
2156
|
+
return Coordinator;
|
|
2157
|
+
})();
|
|
2158
|
+
|
|
2159
|
+
|
|
2160
|
+
var _connect = function (scope) {
|
|
2161
|
+
var renderer = scope._renderer;
|
|
2162
|
+
var coordinator = scope._coordinator;
|
|
2163
|
+
var store = scope._store;
|
|
2164
|
+
|
|
2165
|
+
coordinator.on('renderpartial', renderer.renderPartial, renderer);
|
|
2166
|
+
coordinator.on('renderall', renderer.renderAll, renderer);
|
|
2167
|
+
coordinator.on('extremachange', function (data) {
|
|
2168
|
+
scope._config.onExtremaChange &&
|
|
2169
|
+
scope._config.onExtremaChange({
|
|
2170
|
+
min: data.min,
|
|
2171
|
+
max: data.max,
|
|
2172
|
+
gradient: scope._config['gradient'] || scope._config['defaultGradient']
|
|
2173
|
+
});
|
|
2174
|
+
});
|
|
2175
|
+
store.setCoordinator(coordinator);
|
|
2176
|
+
};
|
|
2177
|
+
|
|
2178
|
+
|
|
2179
|
+
function Heatmap() {
|
|
2180
|
+
var config = this._config = Util.merge(HeatmapConfig, arguments[0] || {});
|
|
2181
|
+
this._coordinator = new Coordinator();
|
|
2182
|
+
if (config['plugin']) {
|
|
2183
|
+
var pluginToLoad = config['plugin'];
|
|
2184
|
+
if (!HeatmapConfig.plugins[pluginToLoad]) {
|
|
2185
|
+
throw new Error('Plugin \'' + pluginToLoad + '\' not found. Maybe it was not registered.');
|
|
2186
|
+
} else {
|
|
2187
|
+
var plugin = HeatmapConfig.plugins[pluginToLoad];
|
|
2188
|
+
// set plugin renderer and store
|
|
2189
|
+
this._renderer = new plugin.renderer(config);
|
|
2190
|
+
this._store = new plugin.store(config);
|
|
2191
|
+
}
|
|
2192
|
+
} else {
|
|
2193
|
+
this._renderer = new Renderer(config);
|
|
2194
|
+
this._store = new Store(config);
|
|
2195
|
+
}
|
|
2196
|
+
_connect(this);
|
|
2197
|
+
}
|
|
2198
|
+
// @TODO:
|
|
2199
|
+
// add API documentation
|
|
2200
|
+
Heatmap.prototype = {
|
|
2201
|
+
addData: function () {
|
|
2202
|
+
this._store.addData.apply(this._store, arguments);
|
|
2203
|
+
return this;
|
|
2204
|
+
},
|
|
2205
|
+
removeData: function () {
|
|
2206
|
+
this._store.removeData && this._store.removeData.apply(this._store, arguments);
|
|
2207
|
+
return this;
|
|
2208
|
+
},
|
|
2209
|
+
setData: function () {
|
|
2210
|
+
this._store.setData.apply(this._store, arguments);
|
|
2211
|
+
return this;
|
|
2212
|
+
},
|
|
2213
|
+
setDataMax: function () {
|
|
2214
|
+
this._store.setDataMax.apply(this._store, arguments);
|
|
2215
|
+
return this;
|
|
2216
|
+
},
|
|
2217
|
+
setDataMin: function () {
|
|
2218
|
+
this._store.setDataMin.apply(this._store, arguments);
|
|
2219
|
+
return this;
|
|
2220
|
+
},
|
|
2221
|
+
configure: function (config) {
|
|
2222
|
+
const oldConfig = this._config;
|
|
2223
|
+
this._config = Util.merge(this._config, config);
|
|
2224
|
+
this._renderer.updateConfig(this._config, oldConfig);
|
|
2225
|
+
this._coordinator.emit('renderall', this._store._getInternalData());
|
|
2226
|
+
return this;
|
|
2227
|
+
},
|
|
2228
|
+
repaint: function () {
|
|
2229
|
+
this._coordinator.emit('renderall', this._store._getInternalData());
|
|
2230
|
+
return this;
|
|
2231
|
+
},
|
|
2232
|
+
getData: function () {
|
|
2233
|
+
return this._store.getData();
|
|
2234
|
+
},
|
|
2235
|
+
getDataURL: function () {
|
|
2236
|
+
return this._renderer.getDataURL();
|
|
2237
|
+
},
|
|
2238
|
+
getValueAt: function (point) {
|
|
2239
|
+
|
|
2240
|
+
if (this._store.getValueAt) {
|
|
2241
|
+
return this._store.getValueAt(point);
|
|
2242
|
+
} else if (this._renderer.getValueAt) {
|
|
2243
|
+
return this._renderer.getValueAt(point);
|
|
2244
|
+
} else {
|
|
2245
|
+
return null;
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
};
|
|
2249
|
+
|
|
2250
|
+
return Heatmap;
|
|
2251
|
+
|
|
2252
|
+
})();
|
|
2253
|
+
|
|
2254
|
+
|
|
2255
|
+
// core
|
|
2256
|
+
var heatmapFactory = {
|
|
2257
|
+
create: function (config) {
|
|
2258
|
+
return new Heatmap(config);
|
|
2259
|
+
},
|
|
2260
|
+
register: function (pluginKey, plugin) {
|
|
2261
|
+
HeatmapConfig.plugins[pluginKey] = plugin;
|
|
2262
|
+
}
|
|
2263
|
+
};
|
|
2264
|
+
|
|
2265
|
+
return heatmapFactory;
|
|
2266
|
+
|
|
2267
|
+
|
|
2268
|
+
})();
|
|
2269
|
+
|
|
2270
|
+
|
|
2271
|
+
|
|
2272
|
+
|
|
2273
|
+
|
|
2274
|
+
|
|
2275
|
+
/*==============================以上部分为heatmap.js的核心代码,只负责热力图的展现====================================*/
|
|
2276
|
+
|
|
2277
|
+
|
|
2278
|
+
/*==============================以下部分为专为百度地图打造的覆盖物===================================================*/
|
|
2279
|
+
/**
|
|
2280
|
+
* @fileoverview 百度地图的热力图功能,对外开放。
|
|
2281
|
+
* 主要基于http://www.patrick-wied.at/static/heatmapjs/index.html 修改而得
|
|
2282
|
+
|
|
2283
|
+
* 主入口类是<a href="symbols/BMapLib.Heatmap.html">Heatmap</a>,
|
|
2284
|
+
* 基于Baidu Map API 2.0。
|
|
2285
|
+
*
|
|
2286
|
+
* @author Baidu Map Api Group
|
|
2287
|
+
* @version 1.0
|
|
2288
|
+
*/
|
|
2289
|
+
|
|
2290
|
+
/**
|
|
2291
|
+
* 热力图的覆盖物
|
|
2292
|
+
* @class 热力图的覆盖物
|
|
2293
|
+
* 实例化该类后,使用map.addOverlay即可以添加热力图
|
|
2294
|
+
*
|
|
2295
|
+
* @constructor
|
|
2296
|
+
* @param {Json Object} opts 可选的输入参数,非必填项。可输入选项包括:<br />
|
|
2297
|
+
* {"<b>radius</b>" : {String} 热力图的半径,
|
|
2298
|
+
* <br />"<b>visible</b>" : {Number} 热力图是否显示,
|
|
2299
|
+
* <br />"<b>gradient</b>" : {JSON} 热力图的渐变区间,
|
|
2300
|
+
* <br />"<b>opacity</b>" : {Number} 热力的透明度,
|
|
2301
|
+
*
|
|
2302
|
+
* @example <b>参考示例:</b><br />
|
|
2303
|
+
* var map = new BMap.Map("container");<br />map.centerAndZoom(new BMap.Point(116.404, 39.915), 15);<br />var heatmapOverlay = new BMapLib.HeatmapOverlay({"radius":10, "visible":true, "opacity":70});<br />heatmapOverlay.setDataSet(data);//data是热力图的详细数据
|
|
2304
|
+
*/
|
|
2305
|
+
|
|
2306
|
+
var HeatmapOverlay = function (opts) {
|
|
2307
|
+
if (Reflect.getPrototypeOf(HeatmapOverlay.prototype) !== BMap.Overlay.prototype) {
|
|
2308
|
+
// 保证让HeatmapOverlay继承BMap.Overlay
|
|
2309
|
+
// 根据文档, 修改原型链会降低访问属性的速度, 但这种写法对代码的修改最少, 故采用
|
|
2310
|
+
// @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf
|
|
2311
|
+
Reflect.setPrototypeOf(HeatmapOverlay.prototype, BMap.Overlay.prototype);
|
|
2312
|
+
}
|
|
2313
|
+
this.conf = opts;
|
|
2314
|
+
this.conf.visible = opts.visible === undefined ? true : opts.visible;
|
|
2315
|
+
this.heatmap = null;
|
|
2316
|
+
this.latlngs = [];
|
|
2317
|
+
this.bounds = null;
|
|
2318
|
+
};
|
|
2319
|
+
|
|
2320
|
+
HeatmapOverlay.prototype = Object.create(typeof BMap === 'undefined' ? null : BMap.Overlay.prototype);
|
|
2321
|
+
|
|
2322
|
+
HeatmapOverlay.prototype.initialize = function (map) {
|
|
2323
|
+
|
|
2324
|
+
this._map = map;
|
|
2325
|
+
var el = document.createElement("div");
|
|
2326
|
+
el.style.position = "absolute";
|
|
2327
|
+
el.style.top = 0;
|
|
2328
|
+
el.style.left = 0;
|
|
2329
|
+
el.style.border = 0;
|
|
2330
|
+
el.style.width = this._map.getSize().width + "px";
|
|
2331
|
+
el.style.height = this._map.getSize().height + "px";
|
|
2332
|
+
this.conf.element = el;
|
|
2333
|
+
|
|
2334
|
+
|
|
2335
|
+
if (!isSupportCanvas()) {//判断是否支持Canvas.
|
|
2336
|
+
return el;
|
|
2337
|
+
}
|
|
2338
|
+
map.getPanes().mapPane.appendChild(el);
|
|
2339
|
+
this.conf.valueField = this.conf.valueField || "count";
|
|
2340
|
+
this.heatmap = heatmapFactory.create(this.conf);
|
|
2341
|
+
|
|
2342
|
+
var that = this;
|
|
2343
|
+
map.addEventListener('resize', function (e) {
|
|
2344
|
+
var size = e.size;
|
|
2345
|
+
el.style.width = size.width + "px";
|
|
2346
|
+
el.style.height = size.height + "px";
|
|
2347
|
+
that.heatmap._renderer.setDimensions(size.width, size.height);
|
|
2348
|
+
that.draw();
|
|
2349
|
+
});
|
|
2350
|
+
|
|
2351
|
+
this._div = el;
|
|
2352
|
+
return el;
|
|
2353
|
+
};
|
|
2354
|
+
|
|
2355
|
+
HeatmapOverlay.prototype.draw = function () {
|
|
2356
|
+
if (!isSupportCanvas()) {//判断是否支持Canvas.
|
|
2357
|
+
return;
|
|
2358
|
+
}
|
|
2359
|
+
var currentBounds = this._map.getBounds();
|
|
2360
|
+
|
|
2361
|
+
if (currentBounds.equals(this.bounds)) {
|
|
2362
|
+
return;
|
|
2363
|
+
}
|
|
2364
|
+
this.bounds = currentBounds;
|
|
2365
|
+
|
|
2366
|
+
var ne = this._map.pointToOverlayPixel(currentBounds.getNorthEast()),
|
|
2367
|
+
sw = this._map.pointToOverlayPixel(currentBounds.getSouthWest()),
|
|
2368
|
+
topY = ne.y,
|
|
2369
|
+
leftX = sw.x,
|
|
2370
|
+
h = sw.y - ne.y,
|
|
2371
|
+
w = ne.x - sw.x;
|
|
2372
|
+
|
|
2373
|
+
this.conf.element.style.left = leftX + 'px';
|
|
2374
|
+
this.conf.element.style.top = topY + 'px';
|
|
2375
|
+
this.conf.element.style.width = w + 'px';
|
|
2376
|
+
this.conf.element.style.height = h + 'px';
|
|
2377
|
+
//this.heatmap.store.get("heatmap").resize();
|
|
2378
|
+
|
|
2379
|
+
if (this.latlngs.length > 0) {
|
|
2380
|
+
this.heatmap.removeData();
|
|
2381
|
+
|
|
2382
|
+
var len = this.latlngs.length;
|
|
2383
|
+
const d = {
|
|
2384
|
+
max: this.heatmap._store.getData().max,
|
|
2385
|
+
data: []
|
|
2386
|
+
};
|
|
2387
|
+
|
|
2388
|
+
while (len--) {
|
|
2389
|
+
var latlng = this.latlngs[len].latlng;
|
|
2390
|
+
|
|
2391
|
+
if (!currentBounds.containsPoint(latlng)) {
|
|
2392
|
+
continue;
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2395
|
+
var divPixel = this._map.pointToOverlayPixel(latlng),
|
|
2396
|
+
leftX = this._map.pointToOverlayPixel(currentBounds.getSouthWest()).x,
|
|
2397
|
+
topY = this._map.pointToOverlayPixel(currentBounds.getNorthEast()).y,
|
|
2398
|
+
screenPixel = new BMap.Pixel(divPixel.x - leftX, divPixel.y - topY);
|
|
2399
|
+
var roundedPoint = this.pixelTransform(screenPixel);
|
|
2400
|
+
d.data.push({
|
|
2401
|
+
x: roundedPoint.x,
|
|
2402
|
+
y: roundedPoint.y,
|
|
2403
|
+
count: this.latlngs[len].c
|
|
2404
|
+
});
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2407
|
+
if (this.conf.radiusChangeByZoom) {
|
|
2408
|
+
this.heatmap._store._cfgRadius = this.conf.radiusChangeByZoom(this._map.getZoom());
|
|
2409
|
+
}
|
|
2410
|
+
this.heatmap.setData(d);
|
|
2411
|
+
}
|
|
2412
|
+
};
|
|
2413
|
+
|
|
2414
|
+
|
|
2415
|
+
//内部使用的坐标转化
|
|
2416
|
+
HeatmapOverlay.prototype.pixelTransform = function (p) {
|
|
2417
|
+
var w = this.heatmap.width,
|
|
2418
|
+
h = this.heatmap.height;
|
|
2419
|
+
|
|
2420
|
+
while (p.x < 0) {
|
|
2421
|
+
p.x += w;
|
|
2422
|
+
}
|
|
2423
|
+
|
|
2424
|
+
while (p.x > w) {
|
|
2425
|
+
p.x -= w;
|
|
2426
|
+
}
|
|
2427
|
+
|
|
2428
|
+
while (p.y < 0) {
|
|
2429
|
+
p.y += h;
|
|
2430
|
+
}
|
|
2431
|
+
|
|
2432
|
+
while (p.y > h) {
|
|
2433
|
+
p.y -= h;
|
|
2434
|
+
}
|
|
2435
|
+
|
|
2436
|
+
p.x = (p.x >> 0);
|
|
2437
|
+
p.y = (p.y >> 0);
|
|
2438
|
+
|
|
2439
|
+
return p;
|
|
2440
|
+
};
|
|
2441
|
+
|
|
2442
|
+
/**
|
|
2443
|
+
* 设置热力图展现的详细数据, 实现之后,即可以立刻展现
|
|
2444
|
+
* @param {Json Object } data
|
|
2445
|
+
* {"<b>max</b>" : {Number} 权重的最大值,
|
|
2446
|
+
* <br />"<b>data</b>" : {Array} 坐标详细数据,格式如下 <br/>
|
|
2447
|
+
* {"lng":116.421969,"lat":39.913527,"count":3}, 其中<br/>
|
|
2448
|
+
* lng lat分别为经纬度, count权重值
|
|
2449
|
+
*/
|
|
2450
|
+
HeatmapOverlay.prototype.setDataSet = function (data) {
|
|
2451
|
+
this.data = data;
|
|
2452
|
+
if (!isSupportCanvas()) {//判断是否支持Canvas.
|
|
2453
|
+
return;
|
|
2454
|
+
}
|
|
2455
|
+
var currentBounds = this._map.getBounds();
|
|
2456
|
+
var mapdata = {
|
|
2457
|
+
max: data.max,
|
|
2458
|
+
data: []
|
|
2459
|
+
};
|
|
2460
|
+
var d = data.data,
|
|
2461
|
+
dlen = d.length;
|
|
2462
|
+
|
|
2463
|
+
|
|
2464
|
+
this.latlngs = [];
|
|
2465
|
+
this.heatmap.removeData();
|
|
2466
|
+
|
|
2467
|
+
if (this.conf.radiusChangeByZoom) {
|
|
2468
|
+
this.heatmap._store._cfgRadius = this.conf.radiusChangeByZoom(this._map.getZoom());
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
while (dlen--) {
|
|
2472
|
+
var latlng = new BMap.Point(d[dlen].lng, d[dlen].lat);
|
|
2473
|
+
this.latlngs.push({
|
|
2474
|
+
latlng: latlng,
|
|
2475
|
+
c: d[dlen].count
|
|
2476
|
+
});
|
|
2477
|
+
|
|
2478
|
+
if (!currentBounds.containsPoint(latlng)) {
|
|
2479
|
+
continue;
|
|
2480
|
+
}
|
|
2481
|
+
|
|
2482
|
+
var divPixel = this._map.pointToOverlayPixel(latlng),
|
|
2483
|
+
leftX = this._map.pointToOverlayPixel(currentBounds.getSouthWest()).x,
|
|
2484
|
+
topY = this._map.pointToOverlayPixel(currentBounds.getNorthEast()).y,
|
|
2485
|
+
screenPixel = new BMap.Pixel(divPixel.x - leftX, divPixel.y - topY);
|
|
2486
|
+
var point = this.pixelTransform(screenPixel);
|
|
2487
|
+
|
|
2488
|
+
mapdata.data.push({
|
|
2489
|
+
x: point.x,
|
|
2490
|
+
y: point.y,
|
|
2491
|
+
count: d[dlen].count
|
|
2492
|
+
});
|
|
2493
|
+
}
|
|
2494
|
+
this.heatmap.setData(mapdata);
|
|
2495
|
+
};
|
|
2496
|
+
|
|
2497
|
+
/**
|
|
2498
|
+
* 添加热力图的详细坐标点
|
|
2499
|
+
* @param {Number} lng 经度坐标
|
|
2500
|
+
* @param {Number} lat 纬度坐标
|
|
2501
|
+
* @param {Number} count 权重
|
|
2502
|
+
*/
|
|
2503
|
+
HeatmapOverlay.prototype.addDataPoint = function (lng, lat, count) {
|
|
2504
|
+
|
|
2505
|
+
if (!isSupportCanvas()) {
|
|
2506
|
+
return;
|
|
2507
|
+
}
|
|
2508
|
+
if (this.data && this.data.data) {
|
|
2509
|
+
this.data.data.push({
|
|
2510
|
+
lng: lng,
|
|
2511
|
+
lat: lat,
|
|
2512
|
+
count: count
|
|
2513
|
+
});
|
|
2514
|
+
}
|
|
2515
|
+
|
|
2516
|
+
var latlng = new BMap.Point(lng, lat),
|
|
2517
|
+
point = this.pixelTransform(this._map.pointToOverlayPixel(latlng));
|
|
2518
|
+
|
|
2519
|
+
this.heatmap.store.addDataPoint(point.x, point.y, count);
|
|
2520
|
+
this.latlngs.push({
|
|
2521
|
+
latlng: latlng,
|
|
2522
|
+
c: count
|
|
2523
|
+
});
|
|
2524
|
+
};
|
|
2525
|
+
|
|
2526
|
+
/**
|
|
2527
|
+
* 更改热力图的展现或者关闭
|
|
2528
|
+
*/
|
|
2529
|
+
|
|
2530
|
+
HeatmapOverlay.prototype.toggle = function () {
|
|
2531
|
+
if (!isSupportCanvas()) {//判断是否支持Canvas.
|
|
2532
|
+
return;
|
|
2533
|
+
}
|
|
2534
|
+
if (this.conf.visible === true) {
|
|
2535
|
+
this.conf.visible = false;
|
|
2536
|
+
} else {
|
|
2537
|
+
this.conf.visible = true;
|
|
2538
|
+
} if (this.conf.visible) {
|
|
2539
|
+
this.conf.element.style.display = "block";
|
|
2540
|
+
} else {
|
|
2541
|
+
this.conf.element.style.display = "none";
|
|
2542
|
+
}
|
|
2543
|
+
};
|
|
2544
|
+
/**
|
|
2545
|
+
* 设置热力图展现的配置
|
|
2546
|
+
* @param {Json Object} options 可选的输入参数,非必填项。可输入选项包括:<br />
|
|
2547
|
+
* {"<b>radius</b>" : {String} 热力图的半径,
|
|
2548
|
+
* <br />"<b>visible</b>" : {Number} 热力图是否显示,
|
|
2549
|
+
* <br />"<b>gradient</b>" : {JSON} 热力图的渐变区间,
|
|
2550
|
+
* <br />"<b>opacity</b>" : {Number} 热力的透明度,}
|
|
2551
|
+
*/
|
|
2552
|
+
HeatmapOverlay.prototype.setOptions = function (options) {
|
|
2553
|
+
if (!isSupportCanvas()) {//判断是否支持Canvas.
|
|
2554
|
+
return;
|
|
2555
|
+
}
|
|
2556
|
+
for (var key in options) {
|
|
2557
|
+
if (key == "radius") {
|
|
2558
|
+
this.heatmap._store._cfgRadius = options[key];
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
this.heatmap.configure(options);
|
|
2562
|
+
if (this.data) {
|
|
2563
|
+
this.setDataSet(this.data);//重新渲染
|
|
2564
|
+
}
|
|
2565
|
+
};
|
|
2566
|
+
|
|
2567
|
+
|
|
2568
|
+
function isSupportCanvas() {
|
|
2569
|
+
var elem = document.createElement('canvas');
|
|
2570
|
+
return !!(elem.getContext && elem.getContext('2d'));
|
|
2571
|
+
}
|
|
2572
|
+
|
|
2573
|
+
return HeatmapOverlay
|
|
2574
|
+
});
|
|
2575
|
+
} (bmaplib_heatmap$1));
|
|
2576
|
+
return bmaplib_heatmap$1.exports;
|
|
2577
|
+
}
|
|
2578
|
+
|
|
2579
|
+
var bmaplib_heatmapExports = requireBmaplib_heatmap();
|
|
2580
|
+
var BHeatMapOverlay = /*@__PURE__*/getDefaultExportFromCjs(bmaplib_heatmapExports);
|
|
2581
|
+
|
|
1571
2582
|
/** 默认半径 */
|
|
1572
2583
|
const RADIUS_DEFAULT = 10;
|
|
1573
2584
|
const HeatmapOptions = {
|
|
@@ -1648,7 +2659,7 @@ class BaiduHeatmap extends Heatmap {
|
|
|
1648
2659
|
super();
|
|
1649
2660
|
this.map = map;
|
|
1650
2661
|
const opts = HeatmapOptions.toBaidu(options, this.map.coordType, options.maxIntensity);
|
|
1651
|
-
this.inner = new
|
|
2662
|
+
this.inner = new BHeatMapOverlay(opts);
|
|
1652
2663
|
// 直接添加到地图上
|
|
1653
2664
|
map.innerMap.addOverlay(this.inner);
|
|
1654
2665
|
// 添加到地图上之后, 才能调用setDataSet
|
|
@@ -1871,6 +2882,3027 @@ class BaiduMapUrls extends MapUrls {
|
|
|
1871
2882
|
}
|
|
1872
2883
|
}
|
|
1873
2884
|
|
|
2885
|
+
/**
|
|
2886
|
+
* Copyright 2019 Google LLC. All Rights Reserved.
|
|
2887
|
+
*
|
|
2888
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
2889
|
+
* you may not use this file except in compliance with the License.
|
|
2890
|
+
* You may obtain a copy of the License at
|
|
2891
|
+
*
|
|
2892
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
2893
|
+
*
|
|
2894
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
2895
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
2896
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
2897
|
+
* See the License for the specific language governing permissions and
|
|
2898
|
+
* limitations under the License.
|
|
2899
|
+
*/
|
|
2900
|
+
|
|
2901
|
+
/**
|
|
2902
|
+
* @name MarkerClusterer for Google Maps v3
|
|
2903
|
+
* @author Luke Mahe
|
|
2904
|
+
* @fileoverview
|
|
2905
|
+
* The library creates and manages per-zoom-level clusters for large amounts of
|
|
2906
|
+
* markers.
|
|
2907
|
+
*/
|
|
2908
|
+
|
|
2909
|
+
/**
|
|
2910
|
+
* A Marker Clusterer that clusters markers.
|
|
2911
|
+
*
|
|
2912
|
+
* @param {google.maps.Map} map The Google map to attach to.
|
|
2913
|
+
* @param {Array.<google.maps.Marker>=} opt_markers Optional markers to add to
|
|
2914
|
+
* the cluster.
|
|
2915
|
+
* @param {Object=} opt_options support the following options:
|
|
2916
|
+
* 'gridSize': (number) The grid size of a cluster in pixels.
|
|
2917
|
+
* 'maxZoom': (number) The maximum zoom level that a marker can be part of a
|
|
2918
|
+
* cluster.
|
|
2919
|
+
* 'zoomOnClick': (boolean) Whether the default behaviour of clicking on a
|
|
2920
|
+
* cluster is to zoom into it.
|
|
2921
|
+
* 'imagePath': (string) The base URL where the images representing
|
|
2922
|
+
* clusters will be found. The full URL will be:
|
|
2923
|
+
* {imagePath}[1-5].{imageExtension}
|
|
2924
|
+
* Default: '../images/m'.
|
|
2925
|
+
* 'imageExtension': (string) The suffix for images URL representing
|
|
2926
|
+
* clusters will be found. See _imagePath_ for details.
|
|
2927
|
+
* Default: 'png'.
|
|
2928
|
+
* 'averageCenter': (boolean) Whether the center of each cluster should be
|
|
2929
|
+
* the average of all markers in the cluster.
|
|
2930
|
+
* 'minimumClusterSize': (number) The minimum number of markers to be in a
|
|
2931
|
+
* cluster before the markers are hidden and a count
|
|
2932
|
+
* is shown.
|
|
2933
|
+
* 'zIndex': (number) the z-index of a cluster.
|
|
2934
|
+
* Default: google.maps.Marker.MAX_ZINDEX + 1
|
|
2935
|
+
* 'styles': (Array.<Object>) An Array of single object that has style properties for all cluster:
|
|
2936
|
+
* 'url': (string) The image url.
|
|
2937
|
+
* 'height': (number) The image height.
|
|
2938
|
+
* 'width': (number) The image width.
|
|
2939
|
+
* 'anchor': (Array) The anchor position of the label text.
|
|
2940
|
+
* 'textColor': (string) The text color.
|
|
2941
|
+
* 'textSize': (number) The text size.
|
|
2942
|
+
* 'backgroundPosition': (string) The position of the backgound x, y.
|
|
2943
|
+
* @constructor
|
|
2944
|
+
* @extends google.maps.OverlayView
|
|
2945
|
+
*/
|
|
2946
|
+
|
|
2947
|
+
let MarkerClusterer$2 = class MarkerClusterer {
|
|
2948
|
+
constructor(map, opt_markers, opt_options) {
|
|
2949
|
+
this.extend(MarkerClusterer, google.maps.OverlayView);
|
|
2950
|
+
this.map_ = map;
|
|
2951
|
+
|
|
2952
|
+
/**
|
|
2953
|
+
* The marker cluster image path.
|
|
2954
|
+
*
|
|
2955
|
+
* @type {string}
|
|
2956
|
+
* @private
|
|
2957
|
+
*/
|
|
2958
|
+
this.MARKER_CLUSTER_IMAGE_PATH_ = "../images/m";
|
|
2959
|
+
|
|
2960
|
+
/**
|
|
2961
|
+
* The marker cluster image path.
|
|
2962
|
+
*
|
|
2963
|
+
* @type {string}
|
|
2964
|
+
* @private
|
|
2965
|
+
*/
|
|
2966
|
+
this.MARKER_CLUSTER_IMAGE_EXTENSION_ = "png";
|
|
2967
|
+
/**
|
|
2968
|
+
* @type {Array.<google.maps.Marker>}
|
|
2969
|
+
* @private
|
|
2970
|
+
*/
|
|
2971
|
+
this.markers_ = [];
|
|
2972
|
+
|
|
2973
|
+
/**
|
|
2974
|
+
* @type {Array.<Cluster>}
|
|
2975
|
+
*/
|
|
2976
|
+
this.clusters_ = [];
|
|
2977
|
+
|
|
2978
|
+
this.sizes = [53, 56, 66, 78, 90];
|
|
2979
|
+
|
|
2980
|
+
/**
|
|
2981
|
+
* @type {Array.<Object>}
|
|
2982
|
+
* @private
|
|
2983
|
+
*/
|
|
2984
|
+
this.styles_ = [];
|
|
2985
|
+
|
|
2986
|
+
/**
|
|
2987
|
+
* @type {boolean}
|
|
2988
|
+
* @private
|
|
2989
|
+
*/
|
|
2990
|
+
this.ready_ = false;
|
|
2991
|
+
|
|
2992
|
+
var options = opt_options || {};
|
|
2993
|
+
|
|
2994
|
+
/**
|
|
2995
|
+
* @type {number}
|
|
2996
|
+
*/
|
|
2997
|
+
this.zIndex_ = options["zIndex"] || google.maps.Marker.MAX_ZINDEX + 1;
|
|
2998
|
+
|
|
2999
|
+
/**
|
|
3000
|
+
* @type {number}
|
|
3001
|
+
* @private
|
|
3002
|
+
*/
|
|
3003
|
+
this.gridSize_ = options["gridSize"] || 60;
|
|
3004
|
+
|
|
3005
|
+
/**
|
|
3006
|
+
* @private
|
|
3007
|
+
*/
|
|
3008
|
+
this.minClusterSize_ = options["minimumClusterSize"] || 2;
|
|
3009
|
+
|
|
3010
|
+
/**
|
|
3011
|
+
* @type {?number}
|
|
3012
|
+
* @private
|
|
3013
|
+
*/
|
|
3014
|
+
this.maxZoom_ = options["maxZoom"] || null;
|
|
3015
|
+
|
|
3016
|
+
this.styles_ = options["styles"] || [];
|
|
3017
|
+
|
|
3018
|
+
/**
|
|
3019
|
+
* @type {string}
|
|
3020
|
+
* @private
|
|
3021
|
+
*/
|
|
3022
|
+
this.imagePath_ = options["imagePath"] || this.MARKER_CLUSTER_IMAGE_PATH_;
|
|
3023
|
+
|
|
3024
|
+
/**
|
|
3025
|
+
* @type {string}
|
|
3026
|
+
* @private
|
|
3027
|
+
*/
|
|
3028
|
+
this.imageExtension_ =
|
|
3029
|
+
options["imageExtension"] || this.MARKER_CLUSTER_IMAGE_EXTENSION_;
|
|
3030
|
+
|
|
3031
|
+
/**
|
|
3032
|
+
* @type {boolean}
|
|
3033
|
+
* @private
|
|
3034
|
+
*/
|
|
3035
|
+
this.zoomOnClick_ = true;
|
|
3036
|
+
|
|
3037
|
+
if (options["zoomOnClick"] != undefined) {
|
|
3038
|
+
this.zoomOnClick_ = options["zoomOnClick"];
|
|
3039
|
+
}
|
|
3040
|
+
|
|
3041
|
+
/**
|
|
3042
|
+
* @type {boolean}
|
|
3043
|
+
* @private
|
|
3044
|
+
*/
|
|
3045
|
+
this.averageCenter_ = false;
|
|
3046
|
+
|
|
3047
|
+
if (options["averageCenter"] != undefined) {
|
|
3048
|
+
this.averageCenter_ = options["averageCenter"];
|
|
3049
|
+
}
|
|
3050
|
+
|
|
3051
|
+
this.setupStyles_();
|
|
3052
|
+
|
|
3053
|
+
this.setMap(map);
|
|
3054
|
+
|
|
3055
|
+
/**
|
|
3056
|
+
* @type {number}
|
|
3057
|
+
* @private
|
|
3058
|
+
*/
|
|
3059
|
+
this.prevZoom_ = this.map_.getZoom();
|
|
3060
|
+
|
|
3061
|
+
// Add the map event listeners
|
|
3062
|
+
var that = this;
|
|
3063
|
+
google.maps.event.addListener(this.map_, "zoom_changed", function() {
|
|
3064
|
+
// Determines map type and prevent illegal zoom levels
|
|
3065
|
+
var zoom = that.map_.getZoom();
|
|
3066
|
+
var minZoom = that.map_.minZoom || 0;
|
|
3067
|
+
var maxZoom = Math.min(
|
|
3068
|
+
that.map_.maxZoom || 100,
|
|
3069
|
+
that.map_.mapTypes[that.map_.getMapTypeId()].maxZoom
|
|
3070
|
+
);
|
|
3071
|
+
zoom = Math.min(Math.max(zoom, minZoom), maxZoom);
|
|
3072
|
+
|
|
3073
|
+
if (that.prevZoom_ != zoom) {
|
|
3074
|
+
that.prevZoom_ = zoom;
|
|
3075
|
+
that.resetViewport();
|
|
3076
|
+
}
|
|
3077
|
+
});
|
|
3078
|
+
|
|
3079
|
+
google.maps.event.addListener(this.map_, "idle", function() {
|
|
3080
|
+
that.redraw();
|
|
3081
|
+
});
|
|
3082
|
+
|
|
3083
|
+
// Finally, add the markers
|
|
3084
|
+
if (
|
|
3085
|
+
opt_markers &&
|
|
3086
|
+
(opt_markers.length || Object.keys(opt_markers).length)
|
|
3087
|
+
) {
|
|
3088
|
+
this.addMarkers(opt_markers, false);
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
3091
|
+
|
|
3092
|
+
/**
|
|
3093
|
+
* Extends a objects prototype by anothers.
|
|
3094
|
+
*
|
|
3095
|
+
* @param {Object} obj1 The object to be extended.
|
|
3096
|
+
* @param {Object} obj2 The object to extend with.
|
|
3097
|
+
* @return {Object} The new extended object.
|
|
3098
|
+
* @ignore
|
|
3099
|
+
*/
|
|
3100
|
+
extend(obj1, obj2) {
|
|
3101
|
+
return function(object) {
|
|
3102
|
+
for (var property in object.prototype) {
|
|
3103
|
+
this.prototype[property] = object.prototype[property];
|
|
3104
|
+
}
|
|
3105
|
+
return this;
|
|
3106
|
+
}.apply(obj1, [obj2]);
|
|
3107
|
+
}
|
|
3108
|
+
|
|
3109
|
+
/**
|
|
3110
|
+
* Implementaion of the interface method.
|
|
3111
|
+
* @ignore
|
|
3112
|
+
*/
|
|
3113
|
+
onAdd() {
|
|
3114
|
+
this.setReady_(true);
|
|
3115
|
+
}
|
|
3116
|
+
|
|
3117
|
+
/**
|
|
3118
|
+
* Implementaion of the interface method.
|
|
3119
|
+
* @ignore
|
|
3120
|
+
*/
|
|
3121
|
+
draw() {}
|
|
3122
|
+
|
|
3123
|
+
/**
|
|
3124
|
+
* Sets up the styles object.
|
|
3125
|
+
*
|
|
3126
|
+
* @private
|
|
3127
|
+
*/
|
|
3128
|
+
setupStyles_() {
|
|
3129
|
+
if (this.styles_.length) {
|
|
3130
|
+
return;
|
|
3131
|
+
}
|
|
3132
|
+
|
|
3133
|
+
for (var i = 0, size; (size = this.sizes[i]); i++) {
|
|
3134
|
+
this.styles_.push({
|
|
3135
|
+
url: this.imagePath_ + (i + 1) + "." + this.imageExtension_,
|
|
3136
|
+
height: size,
|
|
3137
|
+
width: size
|
|
3138
|
+
});
|
|
3139
|
+
}
|
|
3140
|
+
}
|
|
3141
|
+
|
|
3142
|
+
/**
|
|
3143
|
+
* Fit the map to the bounds of the markers in the clusterer.
|
|
3144
|
+
*/
|
|
3145
|
+
fitMapToMarkers() {
|
|
3146
|
+
var markers = this.getMarkers();
|
|
3147
|
+
var bounds = new google.maps.LatLngBounds();
|
|
3148
|
+
for (var i = 0, marker; (marker = markers[i]); i++) {
|
|
3149
|
+
bounds.extend(marker.getPosition());
|
|
3150
|
+
}
|
|
3151
|
+
|
|
3152
|
+
this.map_.fitBounds(bounds);
|
|
3153
|
+
}
|
|
3154
|
+
|
|
3155
|
+
/**
|
|
3156
|
+
* @param {number} zIndex
|
|
3157
|
+
*/
|
|
3158
|
+
setZIndex(zIndex) {
|
|
3159
|
+
this.zIndex_ = zIndex;
|
|
3160
|
+
}
|
|
3161
|
+
|
|
3162
|
+
/**
|
|
3163
|
+
* @return {number}
|
|
3164
|
+
*/
|
|
3165
|
+
getZIndex() {
|
|
3166
|
+
return this.zIndex_;
|
|
3167
|
+
}
|
|
3168
|
+
|
|
3169
|
+
/**
|
|
3170
|
+
* Sets the styles.
|
|
3171
|
+
*
|
|
3172
|
+
* @param {Object} styles The style to set.
|
|
3173
|
+
*/
|
|
3174
|
+
setStyles(styles) {
|
|
3175
|
+
this.styles_ = styles;
|
|
3176
|
+
}
|
|
3177
|
+
|
|
3178
|
+
/**
|
|
3179
|
+
* Gets the styles.
|
|
3180
|
+
*
|
|
3181
|
+
* @return {Object} The styles object.
|
|
3182
|
+
*/
|
|
3183
|
+
getStyles() {
|
|
3184
|
+
return this.styles_;
|
|
3185
|
+
}
|
|
3186
|
+
|
|
3187
|
+
/**
|
|
3188
|
+
* Whether zoom on click is set.
|
|
3189
|
+
*
|
|
3190
|
+
* @return {boolean} True if zoomOnClick_ is set.
|
|
3191
|
+
*/
|
|
3192
|
+
isZoomOnClick() {
|
|
3193
|
+
return this.zoomOnClick_;
|
|
3194
|
+
}
|
|
3195
|
+
|
|
3196
|
+
/**
|
|
3197
|
+
* Whether average center is set.
|
|
3198
|
+
*
|
|
3199
|
+
* @return {boolean} True if averageCenter_ is set.
|
|
3200
|
+
*/
|
|
3201
|
+
isAverageCenter() {
|
|
3202
|
+
return this.averageCenter_;
|
|
3203
|
+
}
|
|
3204
|
+
|
|
3205
|
+
/**
|
|
3206
|
+
* Returns the array of markers in the clusterer.
|
|
3207
|
+
*
|
|
3208
|
+
* @return {Array.<google.maps.Marker>} The markers.
|
|
3209
|
+
*/
|
|
3210
|
+
getMarkers() {
|
|
3211
|
+
return this.markers_;
|
|
3212
|
+
}
|
|
3213
|
+
|
|
3214
|
+
/**
|
|
3215
|
+
* Returns the number of markers in the clusterer
|
|
3216
|
+
*
|
|
3217
|
+
* @return {Number} The number of markers.
|
|
3218
|
+
*/
|
|
3219
|
+
getTotalMarkers() {
|
|
3220
|
+
return this.markers_.length;
|
|
3221
|
+
}
|
|
3222
|
+
|
|
3223
|
+
/**
|
|
3224
|
+
* Sets the max zoom for the clusterer.
|
|
3225
|
+
*
|
|
3226
|
+
* @param {number} maxZoom The max zoom level.
|
|
3227
|
+
*/
|
|
3228
|
+
setMaxZoom(maxZoom) {
|
|
3229
|
+
this.maxZoom_ = maxZoom;
|
|
3230
|
+
}
|
|
3231
|
+
|
|
3232
|
+
/**
|
|
3233
|
+
* Gets the max zoom for the clusterer.
|
|
3234
|
+
*
|
|
3235
|
+
* @return {number} The max zoom level.
|
|
3236
|
+
*/
|
|
3237
|
+
getMaxZoom() {
|
|
3238
|
+
return this.maxZoom_;
|
|
3239
|
+
}
|
|
3240
|
+
|
|
3241
|
+
/**
|
|
3242
|
+
* The function for calculating the cluster icon image.
|
|
3243
|
+
*
|
|
3244
|
+
* @param {Array.<google.maps.Marker>} markers The markers in the clusterer.
|
|
3245
|
+
* @param {number} numStyles The number of styles available.
|
|
3246
|
+
* @return {Object} A object properties: 'text' (string) and 'index' (number).
|
|
3247
|
+
* @private
|
|
3248
|
+
*/
|
|
3249
|
+
calculator_(markers, numStyles) {
|
|
3250
|
+
var index = 0;
|
|
3251
|
+
var count = markers.length;
|
|
3252
|
+
var dv = count;
|
|
3253
|
+
while (dv !== 0) {
|
|
3254
|
+
dv = parseInt(dv / 10, 10);
|
|
3255
|
+
index++;
|
|
3256
|
+
}
|
|
3257
|
+
|
|
3258
|
+
index = Math.min(index, numStyles);
|
|
3259
|
+
return {
|
|
3260
|
+
text: count,
|
|
3261
|
+
index: index
|
|
3262
|
+
};
|
|
3263
|
+
}
|
|
3264
|
+
|
|
3265
|
+
/**
|
|
3266
|
+
* Set the calculator function.
|
|
3267
|
+
*
|
|
3268
|
+
* @param {function(Array, number)} calculator The function to set as the
|
|
3269
|
+
* calculator. The function should return a object properties:
|
|
3270
|
+
* 'text' (string) and 'index' (number).
|
|
3271
|
+
*
|
|
3272
|
+
*/
|
|
3273
|
+
setCalculator(calculator) {
|
|
3274
|
+
this.calculator_ = calculator;
|
|
3275
|
+
}
|
|
3276
|
+
|
|
3277
|
+
/**
|
|
3278
|
+
* Get the calculator function.
|
|
3279
|
+
*
|
|
3280
|
+
* @return {function(Array, number)} the calculator function.
|
|
3281
|
+
*/
|
|
3282
|
+
getCalculator() {
|
|
3283
|
+
return this.calculator_;
|
|
3284
|
+
}
|
|
3285
|
+
|
|
3286
|
+
/**
|
|
3287
|
+
* Add an array of markers to the clusterer.
|
|
3288
|
+
*
|
|
3289
|
+
* @param {Array.<google.maps.Marker>} markers The markers to add.
|
|
3290
|
+
* @param {boolean=} opt_nodraw Whether to redraw the clusters.
|
|
3291
|
+
*/
|
|
3292
|
+
addMarkers(markers, opt_nodraw) {
|
|
3293
|
+
if (markers.length) {
|
|
3294
|
+
for (let i = 0, marker; (marker = markers[i]); i++) {
|
|
3295
|
+
this.pushMarkerTo_(marker);
|
|
3296
|
+
}
|
|
3297
|
+
} else if (Object.keys(markers).length) {
|
|
3298
|
+
for (let marker in markers) {
|
|
3299
|
+
this.pushMarkerTo_(markers[marker]);
|
|
3300
|
+
}
|
|
3301
|
+
}
|
|
3302
|
+
if (!opt_nodraw) {
|
|
3303
|
+
this.redraw();
|
|
3304
|
+
}
|
|
3305
|
+
}
|
|
3306
|
+
|
|
3307
|
+
/**
|
|
3308
|
+
* Pushes a marker to the clusterer.
|
|
3309
|
+
*
|
|
3310
|
+
* @param {google.maps.Marker} marker The marker to add.
|
|
3311
|
+
* @private
|
|
3312
|
+
*/
|
|
3313
|
+
pushMarkerTo_(marker) {
|
|
3314
|
+
marker.isAdded = false;
|
|
3315
|
+
if (marker["draggable"]) {
|
|
3316
|
+
// If the marker is draggable add a listener so we update the clusters on
|
|
3317
|
+
// the drag end.
|
|
3318
|
+
var that = this;
|
|
3319
|
+
google.maps.event.addListener(marker, "dragend", function() {
|
|
3320
|
+
marker.isAdded = false;
|
|
3321
|
+
that.repaint();
|
|
3322
|
+
});
|
|
3323
|
+
}
|
|
3324
|
+
this.markers_.push(marker);
|
|
3325
|
+
}
|
|
3326
|
+
|
|
3327
|
+
/**
|
|
3328
|
+
* Adds a marker to the clusterer and redraws if needed.
|
|
3329
|
+
*
|
|
3330
|
+
* @param {google.maps.Marker} marker The marker to add.
|
|
3331
|
+
* @param {boolean=} opt_nodraw Whether to redraw the clusters.
|
|
3332
|
+
*/
|
|
3333
|
+
addMarker(marker, opt_nodraw) {
|
|
3334
|
+
this.pushMarkerTo_(marker);
|
|
3335
|
+
if (!opt_nodraw) {
|
|
3336
|
+
this.redraw();
|
|
3337
|
+
}
|
|
3338
|
+
}
|
|
3339
|
+
|
|
3340
|
+
/**
|
|
3341
|
+
* Removes a marker and returns true if removed, false if not
|
|
3342
|
+
*
|
|
3343
|
+
* @param {google.maps.Marker} marker The marker to remove
|
|
3344
|
+
* @return {boolean} Whether the marker was removed or not
|
|
3345
|
+
* @private
|
|
3346
|
+
*/
|
|
3347
|
+
removeMarker_(marker) {
|
|
3348
|
+
var index = -1;
|
|
3349
|
+
if (this.markers_.indexOf) {
|
|
3350
|
+
index = this.markers_.indexOf(marker);
|
|
3351
|
+
} else {
|
|
3352
|
+
for (var i = 0, m; (m = this.markers_[i]); i++) {
|
|
3353
|
+
if (m == marker) {
|
|
3354
|
+
index = i;
|
|
3355
|
+
break;
|
|
3356
|
+
}
|
|
3357
|
+
}
|
|
3358
|
+
}
|
|
3359
|
+
|
|
3360
|
+
if (index == -1) {
|
|
3361
|
+
// Marker is not in our list of markers.
|
|
3362
|
+
return false;
|
|
3363
|
+
}
|
|
3364
|
+
|
|
3365
|
+
marker.setMap(null);
|
|
3366
|
+
|
|
3367
|
+
this.markers_.splice(index, 1);
|
|
3368
|
+
|
|
3369
|
+
return true;
|
|
3370
|
+
}
|
|
3371
|
+
|
|
3372
|
+
/**
|
|
3373
|
+
* Remove a marker from the cluster.
|
|
3374
|
+
*
|
|
3375
|
+
* @param {google.maps.Marker} marker The marker to remove.
|
|
3376
|
+
* @param {boolean=} opt_nodraw Optional boolean to force no redraw.
|
|
3377
|
+
* @return {boolean} True if the marker was removed.
|
|
3378
|
+
*/
|
|
3379
|
+
removeMarker(marker, opt_nodraw) {
|
|
3380
|
+
var removed = this.removeMarker_(marker);
|
|
3381
|
+
|
|
3382
|
+
if (!opt_nodraw && removed) {
|
|
3383
|
+
this.resetViewport();
|
|
3384
|
+
this.redraw();
|
|
3385
|
+
return true;
|
|
3386
|
+
} else {
|
|
3387
|
+
return false;
|
|
3388
|
+
}
|
|
3389
|
+
}
|
|
3390
|
+
|
|
3391
|
+
/**
|
|
3392
|
+
* Removes an array of markers from the cluster.
|
|
3393
|
+
*
|
|
3394
|
+
* @param {Array.<google.maps.Marker>} markers The markers to remove.
|
|
3395
|
+
* @param {boolean=} opt_nodraw Optional boolean to force no redraw.
|
|
3396
|
+
*/
|
|
3397
|
+
removeMarkers(markers, opt_nodraw) {
|
|
3398
|
+
// create a local copy of markers if required
|
|
3399
|
+
// (removeMarker_ modifies the getMarkers() array in place)
|
|
3400
|
+
var markersCopy = markers === this.getMarkers() ? markers.slice() : markers;
|
|
3401
|
+
var removed = false;
|
|
3402
|
+
|
|
3403
|
+
for (var i = 0, marker; (marker = markersCopy[i]); i++) {
|
|
3404
|
+
var r = this.removeMarker_(marker);
|
|
3405
|
+
removed = removed || r;
|
|
3406
|
+
}
|
|
3407
|
+
|
|
3408
|
+
if (!opt_nodraw && removed) {
|
|
3409
|
+
this.resetViewport();
|
|
3410
|
+
this.redraw();
|
|
3411
|
+
return true;
|
|
3412
|
+
}
|
|
3413
|
+
}
|
|
3414
|
+
|
|
3415
|
+
/**
|
|
3416
|
+
* Sets the clusterer's ready state.
|
|
3417
|
+
*
|
|
3418
|
+
* @param {boolean} ready The state.
|
|
3419
|
+
* @private
|
|
3420
|
+
*/
|
|
3421
|
+
setReady_(ready) {
|
|
3422
|
+
if (!this.ready_) {
|
|
3423
|
+
this.ready_ = ready;
|
|
3424
|
+
this.createClusters_();
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
3427
|
+
|
|
3428
|
+
/**
|
|
3429
|
+
* Returns the number of clusters in the clusterer.
|
|
3430
|
+
*
|
|
3431
|
+
* @return {number} The number of clusters.
|
|
3432
|
+
*/
|
|
3433
|
+
getTotalClusters() {
|
|
3434
|
+
return this.clusters_.length;
|
|
3435
|
+
}
|
|
3436
|
+
|
|
3437
|
+
/**
|
|
3438
|
+
* Returns the google map that the clusterer is associated with.
|
|
3439
|
+
*
|
|
3440
|
+
* @return {google.maps.Map} The map.
|
|
3441
|
+
*/
|
|
3442
|
+
getMap() {
|
|
3443
|
+
return this.map_;
|
|
3444
|
+
}
|
|
3445
|
+
|
|
3446
|
+
/**
|
|
3447
|
+
* Sets the google map that the clusterer is associated with.
|
|
3448
|
+
*
|
|
3449
|
+
* @param {google.maps.Map} map The map.
|
|
3450
|
+
*/
|
|
3451
|
+
setMap(map) {
|
|
3452
|
+
this.map_ = map;
|
|
3453
|
+
}
|
|
3454
|
+
|
|
3455
|
+
/**
|
|
3456
|
+
* Returns the size of the grid.
|
|
3457
|
+
*
|
|
3458
|
+
* @return {number} The grid size.
|
|
3459
|
+
*/
|
|
3460
|
+
getGridSize() {
|
|
3461
|
+
return this.gridSize_;
|
|
3462
|
+
}
|
|
3463
|
+
|
|
3464
|
+
/**
|
|
3465
|
+
* Sets the size of the grid.
|
|
3466
|
+
*
|
|
3467
|
+
* @param {number} size The grid size.
|
|
3468
|
+
*/
|
|
3469
|
+
setGridSize(size) {
|
|
3470
|
+
this.gridSize_ = size;
|
|
3471
|
+
}
|
|
3472
|
+
|
|
3473
|
+
/**
|
|
3474
|
+
* Returns the min cluster size.
|
|
3475
|
+
*
|
|
3476
|
+
* @return {number} The grid size.
|
|
3477
|
+
*/
|
|
3478
|
+
getMinClusterSize() {
|
|
3479
|
+
return this.minClusterSize_;
|
|
3480
|
+
}
|
|
3481
|
+
|
|
3482
|
+
/**
|
|
3483
|
+
* Sets the min cluster size.
|
|
3484
|
+
*
|
|
3485
|
+
* @param {number} size The grid size.
|
|
3486
|
+
*/
|
|
3487
|
+
setMinClusterSize(size) {
|
|
3488
|
+
this.minClusterSize_ = size;
|
|
3489
|
+
}
|
|
3490
|
+
|
|
3491
|
+
/**
|
|
3492
|
+
* Extends a bounds object by the grid size.
|
|
3493
|
+
*
|
|
3494
|
+
* @param {google.maps.LatLngBounds} bounds The bounds to extend.
|
|
3495
|
+
* @return {google.maps.LatLngBounds} The extended bounds.
|
|
3496
|
+
*/
|
|
3497
|
+
getExtendedBounds(bounds) {
|
|
3498
|
+
var projection = this.getProjection();
|
|
3499
|
+
|
|
3500
|
+
// Turn the bounds into latlng.
|
|
3501
|
+
var tr = new google.maps.LatLng(
|
|
3502
|
+
bounds.getNorthEast().lat(),
|
|
3503
|
+
bounds.getNorthEast().lng()
|
|
3504
|
+
);
|
|
3505
|
+
var bl = new google.maps.LatLng(
|
|
3506
|
+
bounds.getSouthWest().lat(),
|
|
3507
|
+
bounds.getSouthWest().lng()
|
|
3508
|
+
);
|
|
3509
|
+
|
|
3510
|
+
// Convert the points to pixels and the extend out by the grid size.
|
|
3511
|
+
var trPix = projection.fromLatLngToDivPixel(tr);
|
|
3512
|
+
trPix.x += this.gridSize_;
|
|
3513
|
+
trPix.y -= this.gridSize_;
|
|
3514
|
+
|
|
3515
|
+
var blPix = projection.fromLatLngToDivPixel(bl);
|
|
3516
|
+
blPix.x -= this.gridSize_;
|
|
3517
|
+
blPix.y += this.gridSize_;
|
|
3518
|
+
|
|
3519
|
+
// Convert the pixel points back to LatLng
|
|
3520
|
+
var ne = projection.fromDivPixelToLatLng(trPix);
|
|
3521
|
+
var sw = projection.fromDivPixelToLatLng(blPix);
|
|
3522
|
+
|
|
3523
|
+
// Extend the bounds to contain the new bounds.
|
|
3524
|
+
bounds.extend(ne);
|
|
3525
|
+
bounds.extend(sw);
|
|
3526
|
+
|
|
3527
|
+
return bounds;
|
|
3528
|
+
}
|
|
3529
|
+
|
|
3530
|
+
/**
|
|
3531
|
+
* Determins if a marker is contained in a bounds.
|
|
3532
|
+
*
|
|
3533
|
+
* @param {google.maps.Marker} marker The marker to check.
|
|
3534
|
+
* @param {google.maps.LatLngBounds} bounds The bounds to check against.
|
|
3535
|
+
* @return {boolean} True if the marker is in the bounds.
|
|
3536
|
+
* @private
|
|
3537
|
+
*/
|
|
3538
|
+
isMarkerInBounds_(marker, bounds) {
|
|
3539
|
+
return bounds.contains(marker.getPosition());
|
|
3540
|
+
}
|
|
3541
|
+
|
|
3542
|
+
/**
|
|
3543
|
+
* Clears all clusters and markers from the clusterer.
|
|
3544
|
+
*/
|
|
3545
|
+
clearMarkers() {
|
|
3546
|
+
this.resetViewport(true);
|
|
3547
|
+
|
|
3548
|
+
// Set the markers a empty array.
|
|
3549
|
+
this.markers_ = [];
|
|
3550
|
+
}
|
|
3551
|
+
|
|
3552
|
+
/**
|
|
3553
|
+
* Clears all existing clusters and recreates them.
|
|
3554
|
+
* @param {boolean} opt_hide To also hide the marker.
|
|
3555
|
+
*/
|
|
3556
|
+
resetViewport(opt_hide) {
|
|
3557
|
+
// Remove all the clusters
|
|
3558
|
+
for (let i = 0, cluster; (cluster = this.clusters_[i]); i++) {
|
|
3559
|
+
cluster.remove();
|
|
3560
|
+
}
|
|
3561
|
+
|
|
3562
|
+
// Reset the markers to not be added and to be invisible.
|
|
3563
|
+
for (let i = 0, marker; (marker = this.markers_[i]); i++) {
|
|
3564
|
+
marker.isAdded = false;
|
|
3565
|
+
if (opt_hide) {
|
|
3566
|
+
marker.setMap(null);
|
|
3567
|
+
}
|
|
3568
|
+
}
|
|
3569
|
+
|
|
3570
|
+
this.clusters_ = [];
|
|
3571
|
+
}
|
|
3572
|
+
|
|
3573
|
+
/**
|
|
3574
|
+
*
|
|
3575
|
+
*/
|
|
3576
|
+
repaint() {
|
|
3577
|
+
var oldClusters = this.clusters_.slice();
|
|
3578
|
+
this.clusters_.length = 0;
|
|
3579
|
+
this.resetViewport();
|
|
3580
|
+
this.redraw();
|
|
3581
|
+
|
|
3582
|
+
// Remove the old clusters.
|
|
3583
|
+
// Do it in a timeout so the other clusters have been drawn first.
|
|
3584
|
+
setTimeout(function() {
|
|
3585
|
+
for (var i = 0, cluster; (cluster = oldClusters[i]); i++) {
|
|
3586
|
+
cluster.remove();
|
|
3587
|
+
}
|
|
3588
|
+
}, 0);
|
|
3589
|
+
}
|
|
3590
|
+
|
|
3591
|
+
/**
|
|
3592
|
+
* Redraws the clusters.
|
|
3593
|
+
*/
|
|
3594
|
+
redraw() {
|
|
3595
|
+
this.createClusters_();
|
|
3596
|
+
}
|
|
3597
|
+
|
|
3598
|
+
/**
|
|
3599
|
+
* Calculates the distance between two latlng locations in km.
|
|
3600
|
+
* @see http://www.movable-type.co.uk/scripts/latlong.html
|
|
3601
|
+
*
|
|
3602
|
+
* @param {google.maps.LatLng} p1 The first lat lng point.
|
|
3603
|
+
* @param {google.maps.LatLng} p2 The second lat lng point.
|
|
3604
|
+
* @return {number} The distance between the two points in km.
|
|
3605
|
+
* @private
|
|
3606
|
+
*/
|
|
3607
|
+
distanceBetweenPoints_(p1, p2) {
|
|
3608
|
+
if (!p1 || !p2) {
|
|
3609
|
+
return 0;
|
|
3610
|
+
}
|
|
3611
|
+
|
|
3612
|
+
var R = 6371; // Radius of the Earth in km
|
|
3613
|
+
var dLat = ((p2.lat() - p1.lat()) * Math.PI) / 180;
|
|
3614
|
+
var dLon = ((p2.lng() - p1.lng()) * Math.PI) / 180;
|
|
3615
|
+
var a =
|
|
3616
|
+
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
|
3617
|
+
Math.cos((p1.lat() * Math.PI) / 180) *
|
|
3618
|
+
Math.cos((p2.lat() * Math.PI) / 180) *
|
|
3619
|
+
Math.sin(dLon / 2) *
|
|
3620
|
+
Math.sin(dLon / 2);
|
|
3621
|
+
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
3622
|
+
var d = R * c;
|
|
3623
|
+
return d;
|
|
3624
|
+
}
|
|
3625
|
+
|
|
3626
|
+
/**
|
|
3627
|
+
* Add a marker to a cluster, or creates a new cluster.
|
|
3628
|
+
*
|
|
3629
|
+
* @param {google.maps.Marker} marker The marker to add.
|
|
3630
|
+
* @private
|
|
3631
|
+
*/
|
|
3632
|
+
addToClosestCluster_(marker) {
|
|
3633
|
+
var distance = 40000; // Some large number
|
|
3634
|
+
var clusterToAddTo = null;
|
|
3635
|
+
for (var i = 0, cluster; (cluster = this.clusters_[i]); i++) {
|
|
3636
|
+
var center = cluster.getCenter();
|
|
3637
|
+
if (center) {
|
|
3638
|
+
var d = this.distanceBetweenPoints_(center, marker.getPosition());
|
|
3639
|
+
if (d < distance) {
|
|
3640
|
+
distance = d;
|
|
3641
|
+
clusterToAddTo = cluster;
|
|
3642
|
+
}
|
|
3643
|
+
}
|
|
3644
|
+
}
|
|
3645
|
+
|
|
3646
|
+
if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)) {
|
|
3647
|
+
clusterToAddTo.addMarker(marker);
|
|
3648
|
+
} else {
|
|
3649
|
+
var newCluster = new Cluster$1(this);
|
|
3650
|
+
newCluster.addMarker(marker);
|
|
3651
|
+
this.clusters_.push(newCluster);
|
|
3652
|
+
}
|
|
3653
|
+
}
|
|
3654
|
+
|
|
3655
|
+
/**
|
|
3656
|
+
* Creates the clusters.
|
|
3657
|
+
*
|
|
3658
|
+
* @private
|
|
3659
|
+
*/
|
|
3660
|
+
createClusters_() {
|
|
3661
|
+
if (!this.ready_) {
|
|
3662
|
+
return;
|
|
3663
|
+
}
|
|
3664
|
+
|
|
3665
|
+
// Get our current map view bounds.
|
|
3666
|
+
// Create a new bounds object so we don't affect the map.
|
|
3667
|
+
var mapBounds = new google.maps.LatLngBounds(
|
|
3668
|
+
this.map_.getBounds().getSouthWest(),
|
|
3669
|
+
this.map_.getBounds().getNorthEast()
|
|
3670
|
+
);
|
|
3671
|
+
var bounds = this.getExtendedBounds(mapBounds);
|
|
3672
|
+
|
|
3673
|
+
for (var i = 0, marker; (marker = this.markers_[i]); i++) {
|
|
3674
|
+
if (!marker.isAdded && this.isMarkerInBounds_(marker, bounds)) {
|
|
3675
|
+
this.addToClosestCluster_(marker);
|
|
3676
|
+
}
|
|
3677
|
+
}
|
|
3678
|
+
}
|
|
3679
|
+
};
|
|
3680
|
+
|
|
3681
|
+
/**
|
|
3682
|
+
* A cluster that contains markers.
|
|
3683
|
+
*
|
|
3684
|
+
* @param {MarkerClusterer} markerClusterer The markerclusterer that this
|
|
3685
|
+
* cluster is associated with.
|
|
3686
|
+
* @constructor
|
|
3687
|
+
* @ignore
|
|
3688
|
+
*/
|
|
3689
|
+
let Cluster$1 = class Cluster {
|
|
3690
|
+
constructor(markerClusterer) {
|
|
3691
|
+
this.markerClusterer_ = markerClusterer;
|
|
3692
|
+
this.map_ = markerClusterer.getMap();
|
|
3693
|
+
this.gridSize_ = markerClusterer.getGridSize();
|
|
3694
|
+
this.minClusterSize_ = markerClusterer.getMinClusterSize();
|
|
3695
|
+
this.averageCenter_ = markerClusterer.isAverageCenter();
|
|
3696
|
+
this.center_ = null;
|
|
3697
|
+
this.markers_ = [];
|
|
3698
|
+
this.bounds_ = null;
|
|
3699
|
+
this.clusterIcon_ = new ClusterIcon(
|
|
3700
|
+
this,
|
|
3701
|
+
markerClusterer.getStyles(),
|
|
3702
|
+
markerClusterer.getGridSize()
|
|
3703
|
+
);
|
|
3704
|
+
}
|
|
3705
|
+
|
|
3706
|
+
/**
|
|
3707
|
+
* Determins if a marker is already added to the cluster.
|
|
3708
|
+
*
|
|
3709
|
+
* @param {google.maps.Marker} marker The marker to check.
|
|
3710
|
+
* @return {boolean} True if the marker is already added.
|
|
3711
|
+
*/
|
|
3712
|
+
isMarkerAlreadyAdded(marker) {
|
|
3713
|
+
if (this.markers_.indexOf) {
|
|
3714
|
+
return this.markers_.indexOf(marker) != -1;
|
|
3715
|
+
} else {
|
|
3716
|
+
for (var i = 0, m; (m = this.markers_[i]); i++) {
|
|
3717
|
+
if (m == marker) {
|
|
3718
|
+
return true;
|
|
3719
|
+
}
|
|
3720
|
+
}
|
|
3721
|
+
}
|
|
3722
|
+
return false;
|
|
3723
|
+
}
|
|
3724
|
+
|
|
3725
|
+
/**
|
|
3726
|
+
* Add a marker the cluster.
|
|
3727
|
+
*
|
|
3728
|
+
* @param {google.maps.Marker} marker The marker to add.
|
|
3729
|
+
* @return {boolean} True if the marker was added.
|
|
3730
|
+
*/
|
|
3731
|
+
addMarker(marker) {
|
|
3732
|
+
if (this.isMarkerAlreadyAdded(marker)) {
|
|
3733
|
+
return false;
|
|
3734
|
+
}
|
|
3735
|
+
|
|
3736
|
+
if (!this.center_) {
|
|
3737
|
+
this.center_ = marker.getPosition();
|
|
3738
|
+
this.calculateBounds_();
|
|
3739
|
+
} else {
|
|
3740
|
+
if (this.averageCenter_) {
|
|
3741
|
+
var l = this.markers_.length + 1;
|
|
3742
|
+
var lat =
|
|
3743
|
+
(this.center_.lat() * (l - 1) + marker.getPosition().lat()) / l;
|
|
3744
|
+
var lng =
|
|
3745
|
+
(this.center_.lng() * (l - 1) + marker.getPosition().lng()) / l;
|
|
3746
|
+
this.center_ = new google.maps.LatLng(lat, lng);
|
|
3747
|
+
this.calculateBounds_();
|
|
3748
|
+
}
|
|
3749
|
+
}
|
|
3750
|
+
|
|
3751
|
+
marker.isAdded = true;
|
|
3752
|
+
this.markers_.push(marker);
|
|
3753
|
+
|
|
3754
|
+
var len = this.markers_.length;
|
|
3755
|
+
if (len < this.minClusterSize_ && marker.getMap() != this.map_) {
|
|
3756
|
+
// Min cluster size not reached so show the marker.
|
|
3757
|
+
marker.setMap(this.map_);
|
|
3758
|
+
}
|
|
3759
|
+
|
|
3760
|
+
if (len == this.minClusterSize_) {
|
|
3761
|
+
// Hide the markers that were showing.
|
|
3762
|
+
for (var i = 0; i < len; i++) {
|
|
3763
|
+
this.markers_[i].setMap(null);
|
|
3764
|
+
}
|
|
3765
|
+
}
|
|
3766
|
+
|
|
3767
|
+
if (len >= this.minClusterSize_) {
|
|
3768
|
+
marker.setMap(null);
|
|
3769
|
+
}
|
|
3770
|
+
|
|
3771
|
+
this.updateIcon();
|
|
3772
|
+
return true;
|
|
3773
|
+
}
|
|
3774
|
+
|
|
3775
|
+
/**
|
|
3776
|
+
* Returns the marker clusterer that the cluster is associated with.
|
|
3777
|
+
*
|
|
3778
|
+
* @return {MarkerClusterer} The associated marker clusterer.
|
|
3779
|
+
*/
|
|
3780
|
+
getMarkerClusterer() {
|
|
3781
|
+
return this.markerClusterer_;
|
|
3782
|
+
}
|
|
3783
|
+
|
|
3784
|
+
/**
|
|
3785
|
+
* Returns the bounds of the cluster.
|
|
3786
|
+
*
|
|
3787
|
+
* @return {google.maps.LatLngBounds} the cluster bounds.
|
|
3788
|
+
*/
|
|
3789
|
+
getBounds() {
|
|
3790
|
+
var bounds = new google.maps.LatLngBounds(this.center_, this.center_);
|
|
3791
|
+
var markers = this.getMarkers();
|
|
3792
|
+
for (var i = 0, marker; (marker = markers[i]); i++) {
|
|
3793
|
+
bounds.extend(marker.getPosition());
|
|
3794
|
+
}
|
|
3795
|
+
return bounds;
|
|
3796
|
+
}
|
|
3797
|
+
|
|
3798
|
+
/**
|
|
3799
|
+
* Removes the cluster
|
|
3800
|
+
*/
|
|
3801
|
+
remove() {
|
|
3802
|
+
this.clusterIcon_.remove();
|
|
3803
|
+
this.markers_.length = 0;
|
|
3804
|
+
delete this.markers_;
|
|
3805
|
+
}
|
|
3806
|
+
|
|
3807
|
+
/**
|
|
3808
|
+
* Returns the number of markers in the cluster.
|
|
3809
|
+
*
|
|
3810
|
+
* @return {number} The number of markers in the cluster.
|
|
3811
|
+
*/
|
|
3812
|
+
getSize() {
|
|
3813
|
+
return this.markers_.length;
|
|
3814
|
+
}
|
|
3815
|
+
|
|
3816
|
+
/**
|
|
3817
|
+
* Returns a list of the markers in the cluster.
|
|
3818
|
+
*
|
|
3819
|
+
* @return {Array.<google.maps.Marker>} The markers in the cluster.
|
|
3820
|
+
*/
|
|
3821
|
+
getMarkers() {
|
|
3822
|
+
return this.markers_;
|
|
3823
|
+
}
|
|
3824
|
+
|
|
3825
|
+
/**
|
|
3826
|
+
* Returns the center of the cluster.
|
|
3827
|
+
*
|
|
3828
|
+
* @return {google.maps.LatLng} The cluster center.
|
|
3829
|
+
*/
|
|
3830
|
+
getCenter() {
|
|
3831
|
+
return this.center_;
|
|
3832
|
+
}
|
|
3833
|
+
|
|
3834
|
+
/**
|
|
3835
|
+
* Calculated the extended bounds of the cluster with the grid.
|
|
3836
|
+
*
|
|
3837
|
+
* @private
|
|
3838
|
+
*/
|
|
3839
|
+
calculateBounds_() {
|
|
3840
|
+
var bounds = new google.maps.LatLngBounds(this.center_, this.center_);
|
|
3841
|
+
this.bounds_ = this.markerClusterer_.getExtendedBounds(bounds);
|
|
3842
|
+
}
|
|
3843
|
+
|
|
3844
|
+
/**
|
|
3845
|
+
* Determines if a marker lies in the clusters bounds.
|
|
3846
|
+
*
|
|
3847
|
+
* @param {google.maps.Marker} marker The marker to check.
|
|
3848
|
+
* @return {boolean} True if the marker lies in the bounds.
|
|
3849
|
+
*/
|
|
3850
|
+
isMarkerInClusterBounds(marker) {
|
|
3851
|
+
return this.bounds_.contains(marker.getPosition());
|
|
3852
|
+
}
|
|
3853
|
+
|
|
3854
|
+
/**
|
|
3855
|
+
* Returns the map that the cluster is associated with.
|
|
3856
|
+
*
|
|
3857
|
+
* @return {google.maps.Map} The map.
|
|
3858
|
+
*/
|
|
3859
|
+
getMap() {
|
|
3860
|
+
return this.map_;
|
|
3861
|
+
}
|
|
3862
|
+
|
|
3863
|
+
/**
|
|
3864
|
+
* Updates the cluster icon
|
|
3865
|
+
*/
|
|
3866
|
+
updateIcon() {
|
|
3867
|
+
var zoom = this.map_.getZoom();
|
|
3868
|
+
var mz = this.markerClusterer_.getMaxZoom();
|
|
3869
|
+
|
|
3870
|
+
if (mz && zoom > mz) {
|
|
3871
|
+
// The zoom is greater than our max zoom so show all the markers in cluster.
|
|
3872
|
+
for (var i = 0, marker; (marker = this.markers_[i]); i++) {
|
|
3873
|
+
marker.setMap(this.map_);
|
|
3874
|
+
}
|
|
3875
|
+
return;
|
|
3876
|
+
}
|
|
3877
|
+
|
|
3878
|
+
if (this.markers_.length < this.minClusterSize_) {
|
|
3879
|
+
// Min cluster size not yet reached.
|
|
3880
|
+
this.clusterIcon_.hide();
|
|
3881
|
+
return;
|
|
3882
|
+
}
|
|
3883
|
+
|
|
3884
|
+
var numStyles = this.markerClusterer_.getStyles().length;
|
|
3885
|
+
var sums = this.markerClusterer_.getCalculator()(this.markers_, numStyles);
|
|
3886
|
+
this.clusterIcon_.setCenter(this.center_);
|
|
3887
|
+
this.clusterIcon_.setSums(sums);
|
|
3888
|
+
this.clusterIcon_.show();
|
|
3889
|
+
}
|
|
3890
|
+
};
|
|
3891
|
+
|
|
3892
|
+
/**
|
|
3893
|
+
* A cluster icon
|
|
3894
|
+
*
|
|
3895
|
+
* @param {Cluster} cluster The cluster to be associated with.
|
|
3896
|
+
* @param {Object} styles An object that has style properties:
|
|
3897
|
+
* 'url': (string) The image url.
|
|
3898
|
+
* 'height': (number) The image height.
|
|
3899
|
+
* 'width': (number) The image width.
|
|
3900
|
+
* 'anchor': (Array) The anchor position of the label text.
|
|
3901
|
+
* 'textColor': (string) The text color.
|
|
3902
|
+
* 'textSize': (number) The text size.
|
|
3903
|
+
* 'backgroundPosition: (string) The background postition x, y.
|
|
3904
|
+
* @param {number=} opt_padding Optional padding to apply to the cluster icon.
|
|
3905
|
+
* @constructor
|
|
3906
|
+
* @extends google.maps.OverlayView
|
|
3907
|
+
* @ignore
|
|
3908
|
+
*/
|
|
3909
|
+
class ClusterIcon {
|
|
3910
|
+
constructor(cluster, styles, opt_padding) {
|
|
3911
|
+
cluster.getMarkerClusterer().extend(ClusterIcon, google.maps.OverlayView);
|
|
3912
|
+
|
|
3913
|
+
this.styles_ = styles;
|
|
3914
|
+
this.padding_ = opt_padding || 0;
|
|
3915
|
+
this.cluster_ = cluster;
|
|
3916
|
+
this.center_ = null;
|
|
3917
|
+
this.map_ = cluster.getMap();
|
|
3918
|
+
this.div_ = null;
|
|
3919
|
+
this.sums_ = null;
|
|
3920
|
+
this.visible_ = false;
|
|
3921
|
+
|
|
3922
|
+
this.setMap(this.map_);
|
|
3923
|
+
}
|
|
3924
|
+
|
|
3925
|
+
/**
|
|
3926
|
+
* Triggers the clusterclick event and zoom's if the option is set.
|
|
3927
|
+
*/
|
|
3928
|
+
triggerClusterClick() {
|
|
3929
|
+
var clusterBounds = this.cluster_.getBounds();
|
|
3930
|
+
var markerClusterer = this.cluster_.getMarkerClusterer();
|
|
3931
|
+
|
|
3932
|
+
// Trigger the clusterclick event.
|
|
3933
|
+
google.maps.event.trigger(
|
|
3934
|
+
markerClusterer.map_,
|
|
3935
|
+
"clusterclick",
|
|
3936
|
+
this.cluster_
|
|
3937
|
+
);
|
|
3938
|
+
|
|
3939
|
+
if (markerClusterer.isZoomOnClick()) {
|
|
3940
|
+
// Zoom into the cluster.
|
|
3941
|
+
this.map_.fitBounds(clusterBounds);
|
|
3942
|
+
this.map_.setCenter(clusterBounds.getCenter());
|
|
3943
|
+
}
|
|
3944
|
+
}
|
|
3945
|
+
|
|
3946
|
+
/**
|
|
3947
|
+
* Adding the cluster icon to the dom.
|
|
3948
|
+
* @ignore
|
|
3949
|
+
*/
|
|
3950
|
+
onAdd() {
|
|
3951
|
+
this.div_ = document.createElement("DIV");
|
|
3952
|
+
if (this.visible_) {
|
|
3953
|
+
var pos = this.getPosFromLatLng_(this.center_);
|
|
3954
|
+
this.div_.style.cssText = this.createCss(pos);
|
|
3955
|
+
this.div_.innerHTML = this.sums_.text;
|
|
3956
|
+
}
|
|
3957
|
+
|
|
3958
|
+
var panes = this.getPanes();
|
|
3959
|
+
panes.overlayMouseTarget.appendChild(this.div_);
|
|
3960
|
+
|
|
3961
|
+
var that = this;
|
|
3962
|
+
google.maps.event.addDomListener(this.div_, "click", function() {
|
|
3963
|
+
that.triggerClusterClick();
|
|
3964
|
+
});
|
|
3965
|
+
}
|
|
3966
|
+
|
|
3967
|
+
/**
|
|
3968
|
+
* Returns the position to place the div dending on the latlng.
|
|
3969
|
+
*
|
|
3970
|
+
* @param {google.maps.LatLng} latlng The position in latlng.
|
|
3971
|
+
* @return {google.maps.Point} The position in pixels.
|
|
3972
|
+
* @private
|
|
3973
|
+
*/
|
|
3974
|
+
getPosFromLatLng_(latlng) {
|
|
3975
|
+
var pos = this.getProjection().fromLatLngToDivPixel(latlng);
|
|
3976
|
+
pos.x -= parseInt(this.width_ / 2, 10);
|
|
3977
|
+
pos.y -= parseInt(this.height_ / 2, 10);
|
|
3978
|
+
return pos;
|
|
3979
|
+
}
|
|
3980
|
+
|
|
3981
|
+
/**
|
|
3982
|
+
* Draw the icon.
|
|
3983
|
+
* @ignore
|
|
3984
|
+
*/
|
|
3985
|
+
draw() {
|
|
3986
|
+
if (this.visible_) {
|
|
3987
|
+
var pos = this.getPosFromLatLng_(this.center_);
|
|
3988
|
+
this.div_.style.top = pos.y + "px";
|
|
3989
|
+
this.div_.style.left = pos.x + "px";
|
|
3990
|
+
}
|
|
3991
|
+
}
|
|
3992
|
+
|
|
3993
|
+
/**
|
|
3994
|
+
* Hide the icon.
|
|
3995
|
+
*/
|
|
3996
|
+
hide() {
|
|
3997
|
+
if (this.div_) {
|
|
3998
|
+
this.div_.style.display = "none";
|
|
3999
|
+
}
|
|
4000
|
+
this.visible_ = false;
|
|
4001
|
+
}
|
|
4002
|
+
|
|
4003
|
+
/**
|
|
4004
|
+
* Position and show the icon.
|
|
4005
|
+
*/
|
|
4006
|
+
show() {
|
|
4007
|
+
if (this.div_) {
|
|
4008
|
+
var pos = this.getPosFromLatLng_(this.center_);
|
|
4009
|
+
this.div_.style.cssText = this.createCss(pos);
|
|
4010
|
+
this.div_.style.display = "";
|
|
4011
|
+
}
|
|
4012
|
+
this.visible_ = true;
|
|
4013
|
+
}
|
|
4014
|
+
|
|
4015
|
+
/**
|
|
4016
|
+
* Remove the icon from the map
|
|
4017
|
+
*/
|
|
4018
|
+
remove() {
|
|
4019
|
+
this.setMap(null);
|
|
4020
|
+
}
|
|
4021
|
+
|
|
4022
|
+
/**
|
|
4023
|
+
* Implementation of the onRemove interface.
|
|
4024
|
+
* @ignore
|
|
4025
|
+
*/
|
|
4026
|
+
onRemove() {
|
|
4027
|
+
if (this.div_ && this.div_.parentNode) {
|
|
4028
|
+
this.hide();
|
|
4029
|
+
this.div_.parentNode.removeChild(this.div_);
|
|
4030
|
+
this.div_ = null;
|
|
4031
|
+
}
|
|
4032
|
+
}
|
|
4033
|
+
|
|
4034
|
+
/**
|
|
4035
|
+
* Set the sums of the icon.
|
|
4036
|
+
*
|
|
4037
|
+
* @param {Object} sums The sums containing:
|
|
4038
|
+
* 'text': (string) The text to display in the icon.
|
|
4039
|
+
* 'index': (number) The style index of the icon.
|
|
4040
|
+
*/
|
|
4041
|
+
setSums(sums) {
|
|
4042
|
+
this.sums_ = sums;
|
|
4043
|
+
this.text_ = sums.text;
|
|
4044
|
+
this.index_ = sums.index;
|
|
4045
|
+
if (this.div_) {
|
|
4046
|
+
this.div_.innerHTML = sums.text;
|
|
4047
|
+
}
|
|
4048
|
+
|
|
4049
|
+
this.useStyle();
|
|
4050
|
+
}
|
|
4051
|
+
|
|
4052
|
+
/**
|
|
4053
|
+
* Sets the icon to the the styles.
|
|
4054
|
+
*/
|
|
4055
|
+
useStyle() {
|
|
4056
|
+
var index = Math.max(0, this.sums_.index - 1);
|
|
4057
|
+
index = Math.min(this.styles_.length - 1, index);
|
|
4058
|
+
var style = this.styles_[index];
|
|
4059
|
+
this.url_ = style["url"];
|
|
4060
|
+
this.height_ = style["height"];
|
|
4061
|
+
this.width_ = style["width"];
|
|
4062
|
+
this.textColor_ = style["textColor"];
|
|
4063
|
+
this.anchor_ = style["anchor"];
|
|
4064
|
+
this.textSize_ = style["textSize"];
|
|
4065
|
+
this.backgroundPosition_ = style["backgroundPosition"];
|
|
4066
|
+
}
|
|
4067
|
+
|
|
4068
|
+
/**
|
|
4069
|
+
* Sets the center of the icon.
|
|
4070
|
+
*
|
|
4071
|
+
* @param {google.maps.LatLng} center The latlng to set as the center.
|
|
4072
|
+
*/
|
|
4073
|
+
setCenter(center) {
|
|
4074
|
+
this.center_ = center;
|
|
4075
|
+
}
|
|
4076
|
+
|
|
4077
|
+
/**
|
|
4078
|
+
* Create the css text based on the position of the icon.
|
|
4079
|
+
*
|
|
4080
|
+
* @param {google.maps.Point} pos The position.
|
|
4081
|
+
* @return {string} The css style text.
|
|
4082
|
+
*/
|
|
4083
|
+
createCss(pos) {
|
|
4084
|
+
var style = [];
|
|
4085
|
+
style.push("z-index:" + this.cluster_.markerClusterer_.getZIndex() + ";");
|
|
4086
|
+
style.push("background-image:url(" + this.url_ + ");");
|
|
4087
|
+
var backgroundPosition = this.backgroundPosition_
|
|
4088
|
+
? this.backgroundPosition_
|
|
4089
|
+
: "0 0";
|
|
4090
|
+
style.push("background-position:" + backgroundPosition + ";");
|
|
4091
|
+
|
|
4092
|
+
if (typeof this.anchor_ === "object") {
|
|
4093
|
+
if (
|
|
4094
|
+
typeof this.anchor_[0] === "number" &&
|
|
4095
|
+
this.anchor_[0] > 0 &&
|
|
4096
|
+
this.anchor_[0] < this.height_
|
|
4097
|
+
) {
|
|
4098
|
+
style.push(
|
|
4099
|
+
"height:" +
|
|
4100
|
+
(this.height_ - this.anchor_[0]) +
|
|
4101
|
+
"px; padding-top:" +
|
|
4102
|
+
this.anchor_[0] +
|
|
4103
|
+
"px;"
|
|
4104
|
+
);
|
|
4105
|
+
} else {
|
|
4106
|
+
style.push(
|
|
4107
|
+
"height:" + this.height_ + "px; line-height:" + this.height_ + "px;"
|
|
4108
|
+
);
|
|
4109
|
+
}
|
|
4110
|
+
if (
|
|
4111
|
+
typeof this.anchor_[1] === "number" &&
|
|
4112
|
+
this.anchor_[1] > 0 &&
|
|
4113
|
+
this.anchor_[1] < this.width_
|
|
4114
|
+
) {
|
|
4115
|
+
style.push(
|
|
4116
|
+
"width:" +
|
|
4117
|
+
(this.width_ - this.anchor_[1]) +
|
|
4118
|
+
"px; padding-left:" +
|
|
4119
|
+
this.anchor_[1] +
|
|
4120
|
+
"px;"
|
|
4121
|
+
);
|
|
4122
|
+
} else {
|
|
4123
|
+
style.push("width:" + this.width_ + "px; text-align:center;");
|
|
4124
|
+
}
|
|
4125
|
+
} else {
|
|
4126
|
+
style.push(
|
|
4127
|
+
"height:" +
|
|
4128
|
+
this.height_ +
|
|
4129
|
+
"px; line-height:" +
|
|
4130
|
+
this.height_ +
|
|
4131
|
+
"px; width:" +
|
|
4132
|
+
this.width_ +
|
|
4133
|
+
"px; text-align:center;"
|
|
4134
|
+
);
|
|
4135
|
+
}
|
|
4136
|
+
|
|
4137
|
+
var txtColor = this.textColor_ ? this.textColor_ : "black";
|
|
4138
|
+
var txtSize = this.textSize_ ? this.textSize_ : 11;
|
|
4139
|
+
|
|
4140
|
+
style.push(
|
|
4141
|
+
"cursor:pointer; top:" +
|
|
4142
|
+
pos.y +
|
|
4143
|
+
"px; left:" +
|
|
4144
|
+
pos.x +
|
|
4145
|
+
"px; color:" +
|
|
4146
|
+
txtColor +
|
|
4147
|
+
"; position:absolute; font-size:" +
|
|
4148
|
+
txtSize +
|
|
4149
|
+
"px; font-family:Arial,sans-serif; font-weight:bold"
|
|
4150
|
+
);
|
|
4151
|
+
return style.join("");
|
|
4152
|
+
}
|
|
4153
|
+
}
|
|
4154
|
+
|
|
4155
|
+
var bmaplib_texticonoverlay$1 = {exports: {}};
|
|
4156
|
+
|
|
4157
|
+
var bmaplib_texticonoverlay = bmaplib_texticonoverlay$1.exports;
|
|
4158
|
+
|
|
4159
|
+
var hasRequiredBmaplib_texticonoverlay;
|
|
4160
|
+
|
|
4161
|
+
function requireBmaplib_texticonoverlay () {
|
|
4162
|
+
if (hasRequiredBmaplib_texticonoverlay) return bmaplib_texticonoverlay$1.exports;
|
|
4163
|
+
hasRequiredBmaplib_texticonoverlay = 1;
|
|
4164
|
+
(function (module, exports) {
|
|
4165
|
+
(function (root, factory) {
|
|
4166
|
+
{
|
|
4167
|
+
module.exports = factory();
|
|
4168
|
+
}
|
|
4169
|
+
})(bmaplib_texticonoverlay, function() {
|
|
4170
|
+
var T,
|
|
4171
|
+
baidu = T = baidu || {version: "1.3.8"};
|
|
4172
|
+
var context = {};
|
|
4173
|
+
//提出guid,防止在与老版本Tangram混用时
|
|
4174
|
+
//在下一行错误的修改context[undefined]
|
|
4175
|
+
baidu.guid = "$BAIDU$";
|
|
4176
|
+
|
|
4177
|
+
//Tangram可能被放在闭包中
|
|
4178
|
+
//一些页面级别唯一的属性,需要挂载在context[baidu.guid]上
|
|
4179
|
+
context[baidu.guid] = context[baidu.guid] || {};
|
|
4180
|
+
|
|
4181
|
+
/**
|
|
4182
|
+
* @ignore
|
|
4183
|
+
* @namespace baidu.dom 操作dom的方法。
|
|
4184
|
+
*/
|
|
4185
|
+
baidu.dom = baidu.dom || {};
|
|
4186
|
+
|
|
4187
|
+
|
|
4188
|
+
/**
|
|
4189
|
+
* 从文档中获取指定的DOM元素
|
|
4190
|
+
* @name baidu.dom.g
|
|
4191
|
+
* @function
|
|
4192
|
+
* @grammar baidu.dom.g(id)
|
|
4193
|
+
* @param {string|HTMLElement} id 元素的id或DOM元素
|
|
4194
|
+
* @shortcut g,T.G
|
|
4195
|
+
* @meta standard
|
|
4196
|
+
* @see baidu.dom.q
|
|
4197
|
+
*
|
|
4198
|
+
* @returns {HTMLElement|null} 获取的元素,查找不到时返回null,如果参数不合法,直接返回参数
|
|
4199
|
+
*/
|
|
4200
|
+
baidu.dom.g = function (id) {
|
|
4201
|
+
if ('string' == typeof id || id instanceof String) {
|
|
4202
|
+
return document.getElementById(id);
|
|
4203
|
+
} else if (id && id.nodeName && (id.nodeType == 1 || id.nodeType == 9)) {
|
|
4204
|
+
return id;
|
|
4205
|
+
}
|
|
4206
|
+
return null;
|
|
4207
|
+
};
|
|
4208
|
+
|
|
4209
|
+
// 声明快捷方法
|
|
4210
|
+
baidu.g = baidu.G = baidu.dom.g;
|
|
4211
|
+
|
|
4212
|
+
/**
|
|
4213
|
+
* 获取目标元素所属的document对象
|
|
4214
|
+
* @name baidu.dom.getDocument
|
|
4215
|
+
* @function
|
|
4216
|
+
* @grammar baidu.dom.getDocument(element)
|
|
4217
|
+
* @param {HTMLElement|string} element 目标元素或目标元素的id
|
|
4218
|
+
* @meta standard
|
|
4219
|
+
* @see baidu.dom.getWindow
|
|
4220
|
+
*
|
|
4221
|
+
* @returns {HTMLDocument} 目标元素所属的document对象
|
|
4222
|
+
*/
|
|
4223
|
+
baidu.dom.getDocument = function (element) {
|
|
4224
|
+
element = baidu.dom.g(element);
|
|
4225
|
+
return element.nodeType == 9 ? element : element.ownerDocument || element.document;
|
|
4226
|
+
};
|
|
4227
|
+
|
|
4228
|
+
/**
|
|
4229
|
+
* @ignore
|
|
4230
|
+
* @namespace baidu.lang 对语言层面的封装,包括类型判断、模块扩展、继承基类以及对象自定义事件的支持。
|
|
4231
|
+
*/
|
|
4232
|
+
baidu.lang = baidu.lang || {};
|
|
4233
|
+
|
|
4234
|
+
/**
|
|
4235
|
+
* 判断目标参数是否string类型或String对象
|
|
4236
|
+
* @name baidu.lang.isString
|
|
4237
|
+
* @function
|
|
4238
|
+
* @grammar baidu.lang.isString(source)
|
|
4239
|
+
* @param {Any} source 目标参数
|
|
4240
|
+
* @shortcut isString
|
|
4241
|
+
* @meta standard
|
|
4242
|
+
* @see baidu.lang.isObject,baidu.lang.isNumber,baidu.lang.isArray,baidu.lang.isElement,baidu.lang.isBoolean,baidu.lang.isDate
|
|
4243
|
+
*
|
|
4244
|
+
* @returns {boolean} 类型判断结果
|
|
4245
|
+
*/
|
|
4246
|
+
baidu.lang.isString = function (source) {
|
|
4247
|
+
return '[object String]' == Object.prototype.toString.call(source);
|
|
4248
|
+
};
|
|
4249
|
+
|
|
4250
|
+
// 声明快捷方法
|
|
4251
|
+
baidu.isString = baidu.lang.isString;
|
|
4252
|
+
|
|
4253
|
+
/**
|
|
4254
|
+
* 从文档中获取指定的DOM元素
|
|
4255
|
+
* **内部方法**
|
|
4256
|
+
*
|
|
4257
|
+
* @param {string|HTMLElement} id 元素的id或DOM元素
|
|
4258
|
+
* @meta standard
|
|
4259
|
+
* @return {HTMLElement} DOM元素,如果不存在,返回null,如果参数不合法,直接返回参数
|
|
4260
|
+
*/
|
|
4261
|
+
baidu.dom._g = function (id) {
|
|
4262
|
+
if (baidu.lang.isString(id)) {
|
|
4263
|
+
return document.getElementById(id);
|
|
4264
|
+
}
|
|
4265
|
+
return id;
|
|
4266
|
+
};
|
|
4267
|
+
|
|
4268
|
+
// 声明快捷方法
|
|
4269
|
+
baidu._g = baidu.dom._g;
|
|
4270
|
+
|
|
4271
|
+
/**
|
|
4272
|
+
* @ignore
|
|
4273
|
+
* @namespace baidu.browser 判断浏览器类型和特性的属性。
|
|
4274
|
+
*/
|
|
4275
|
+
baidu.browser = baidu.browser || {};
|
|
4276
|
+
|
|
4277
|
+
if (/msie (\d+\.\d)/i.test(navigator.userAgent)) {
|
|
4278
|
+
//IE 8下,以documentMode为准
|
|
4279
|
+
//在百度模板中,可能会有$,防止冲突,将$1 写成 \x241
|
|
4280
|
+
/**
|
|
4281
|
+
* 判断是否为ie浏览器
|
|
4282
|
+
* @property ie ie版本号
|
|
4283
|
+
* @grammar baidu.browser.ie
|
|
4284
|
+
* @meta standard
|
|
4285
|
+
* @shortcut ie
|
|
4286
|
+
* @see baidu.browser.firefox,baidu.browser.safari,baidu.browser.opera,baidu.browser.chrome,baidu.browser.maxthon
|
|
4287
|
+
*/
|
|
4288
|
+
baidu.browser.ie = baidu.ie = document.documentMode || + RegExp['\x241'];
|
|
4289
|
+
}
|
|
4290
|
+
|
|
4291
|
+
/**
|
|
4292
|
+
* 获取目标元素的computed style值。如果元素的样式值不能被浏览器计算,则会返回空字符串(IE)
|
|
4293
|
+
*
|
|
4294
|
+
* @author berg
|
|
4295
|
+
* @name baidu.dom.getComputedStyle
|
|
4296
|
+
* @function
|
|
4297
|
+
* @grammar baidu.dom.getComputedStyle(element, key)
|
|
4298
|
+
* @param {HTMLElement|string} element 目标元素或目标元素的id
|
|
4299
|
+
* @param {string} key 要获取的样式名
|
|
4300
|
+
*
|
|
4301
|
+
* @see baidu.dom.getStyle
|
|
4302
|
+
*
|
|
4303
|
+
* @returns {string} 目标元素的computed style值
|
|
4304
|
+
*/
|
|
4305
|
+
|
|
4306
|
+
baidu.dom.getComputedStyle = function(element, key){
|
|
4307
|
+
element = baidu.dom._g(element);
|
|
4308
|
+
var doc = baidu.dom.getDocument(element),
|
|
4309
|
+
styles;
|
|
4310
|
+
if (doc.defaultView && doc.defaultView.getComputedStyle) {
|
|
4311
|
+
styles = doc.defaultView.getComputedStyle(element, null);
|
|
4312
|
+
if (styles) {
|
|
4313
|
+
return styles[key] || styles.getPropertyValue(key);
|
|
4314
|
+
}
|
|
4315
|
+
}
|
|
4316
|
+
return '';
|
|
4317
|
+
};
|
|
4318
|
+
|
|
4319
|
+
/**
|
|
4320
|
+
* 提供给setStyle与getStyle使用
|
|
4321
|
+
*/
|
|
4322
|
+
baidu.dom._styleFixer = baidu.dom._styleFixer || {};
|
|
4323
|
+
|
|
4324
|
+
/**
|
|
4325
|
+
* 提供给setStyle与getStyle使用
|
|
4326
|
+
*/
|
|
4327
|
+
baidu.dom._styleFilter = baidu.dom._styleFilter || [];
|
|
4328
|
+
|
|
4329
|
+
/**
|
|
4330
|
+
* 为获取和设置样式的过滤器
|
|
4331
|
+
* @private
|
|
4332
|
+
* @meta standard
|
|
4333
|
+
*/
|
|
4334
|
+
baidu.dom._styleFilter.filter = function (key, value, method) {
|
|
4335
|
+
for (var i = 0, filters = baidu.dom._styleFilter, filter; filter = filters[i]; i++) {
|
|
4336
|
+
if (filter = filter[method]) {
|
|
4337
|
+
value = filter(key, value);
|
|
4338
|
+
}
|
|
4339
|
+
}
|
|
4340
|
+
return value;
|
|
4341
|
+
};
|
|
4342
|
+
|
|
4343
|
+
/**
|
|
4344
|
+
* @ignore
|
|
4345
|
+
* @namespace baidu.string 操作字符串的方法。
|
|
4346
|
+
*/
|
|
4347
|
+
baidu.string = baidu.string || {};
|
|
4348
|
+
|
|
4349
|
+
/**
|
|
4350
|
+
* 将目标字符串进行驼峰化处理
|
|
4351
|
+
* @name baidu.string.toCamelCase
|
|
4352
|
+
* @function
|
|
4353
|
+
* @grammar baidu.string.toCamelCase(source)
|
|
4354
|
+
* @param {string} source 目标字符串
|
|
4355
|
+
* @remark
|
|
4356
|
+
* 支持单词以“-_”分隔
|
|
4357
|
+
* @meta standard
|
|
4358
|
+
*
|
|
4359
|
+
* @returns {string} 驼峰化处理后的字符串
|
|
4360
|
+
*/
|
|
4361
|
+
baidu.string.toCamelCase = function (source) {
|
|
4362
|
+
//提前判断,提高getStyle等的效率 thanks xianwei
|
|
4363
|
+
if (source.indexOf('-') < 0 && source.indexOf('_') < 0) {
|
|
4364
|
+
return source;
|
|
4365
|
+
}
|
|
4366
|
+
return source.replace(/[-_][^-_]/g, function (match) {
|
|
4367
|
+
return match.charAt(1).toUpperCase();
|
|
4368
|
+
});
|
|
4369
|
+
};
|
|
4370
|
+
|
|
4371
|
+
/**
|
|
4372
|
+
* 获取目标元素的样式值
|
|
4373
|
+
* @name baidu.dom.getStyle
|
|
4374
|
+
* @function
|
|
4375
|
+
* @grammar baidu.dom.getStyle(element, key)
|
|
4376
|
+
* @param {HTMLElement|string} element 目标元素或目标元素的id
|
|
4377
|
+
* @param {string} key 要获取的样式名
|
|
4378
|
+
* @remark
|
|
4379
|
+
*
|
|
4380
|
+
* 为了精简代码,本模块默认不对任何浏览器返回值进行归一化处理(如使用getStyle时,不同浏览器下可能返回rgb颜色或hex颜色),也不会修复浏览器的bug和差异性(如设置IE的float属性叫styleFloat,firefox则是cssFloat)。<br />
|
|
4381
|
+
* baidu.dom._styleFixer和baidu.dom._styleFilter可以为本模块提供支持。<br />
|
|
4382
|
+
* 其中_styleFilter能对颜色和px进行归一化处理,_styleFixer能对display,float,opacity,textOverflow的浏览器兼容性bug进行处理。
|
|
4383
|
+
* @shortcut getStyle
|
|
4384
|
+
* @meta standard
|
|
4385
|
+
* @see baidu.dom.setStyle,baidu.dom.setStyles, baidu.dom.getComputedStyle
|
|
4386
|
+
*
|
|
4387
|
+
* @returns {string} 目标元素的样式值
|
|
4388
|
+
*/
|
|
4389
|
+
baidu.dom.getStyle = function (element, key) {
|
|
4390
|
+
var dom = baidu.dom;
|
|
4391
|
+
|
|
4392
|
+
element = dom.g(element);
|
|
4393
|
+
key = baidu.string.toCamelCase(key);
|
|
4394
|
+
//computed style, then cascaded style, then explicitly set style.
|
|
4395
|
+
var value = element.style[key] ||
|
|
4396
|
+
(element.currentStyle ? element.currentStyle[key] : "") ||
|
|
4397
|
+
dom.getComputedStyle(element, key);
|
|
4398
|
+
|
|
4399
|
+
// 在取不到值的时候,用fixer进行修正
|
|
4400
|
+
if (!value) {
|
|
4401
|
+
var fixer = dom._styleFixer[key];
|
|
4402
|
+
if(fixer){
|
|
4403
|
+
value = fixer.get ? fixer.get(element) : baidu.dom.getStyle(element, fixer);
|
|
4404
|
+
}
|
|
4405
|
+
}
|
|
4406
|
+
|
|
4407
|
+
/* 检查结果过滤器 */
|
|
4408
|
+
if (fixer = dom._styleFilter) {
|
|
4409
|
+
value = fixer.filter(key, value, 'get');
|
|
4410
|
+
}
|
|
4411
|
+
|
|
4412
|
+
return value;
|
|
4413
|
+
};
|
|
4414
|
+
|
|
4415
|
+
// 声明快捷方法
|
|
4416
|
+
baidu.getStyle = baidu.dom.getStyle;
|
|
4417
|
+
|
|
4418
|
+
|
|
4419
|
+
if (/opera\/(\d+\.\d)/i.test(navigator.userAgent)) {
|
|
4420
|
+
/**
|
|
4421
|
+
* 判断是否为opera浏览器
|
|
4422
|
+
* @property opera opera版本号
|
|
4423
|
+
* @grammar baidu.browser.opera
|
|
4424
|
+
* @meta standard
|
|
4425
|
+
* @see baidu.browser.ie,baidu.browser.firefox,baidu.browser.safari,baidu.browser.chrome
|
|
4426
|
+
*/
|
|
4427
|
+
baidu.browser.opera = + RegExp['\x241'];
|
|
4428
|
+
}
|
|
4429
|
+
|
|
4430
|
+
/**
|
|
4431
|
+
* 判断是否为webkit内核
|
|
4432
|
+
* @property isWebkit
|
|
4433
|
+
* @grammar baidu.browser.isWebkit
|
|
4434
|
+
* @meta standard
|
|
4435
|
+
* @see baidu.browser.isGecko
|
|
4436
|
+
*/
|
|
4437
|
+
baidu.browser.isWebkit = /webkit/i.test(navigator.userAgent);
|
|
4438
|
+
|
|
4439
|
+
/**
|
|
4440
|
+
* 判断是否为gecko内核
|
|
4441
|
+
* @property isGecko
|
|
4442
|
+
* @grammar baidu.browser.isGecko
|
|
4443
|
+
* @meta standard
|
|
4444
|
+
* @see baidu.browser.isWebkit
|
|
4445
|
+
*/
|
|
4446
|
+
baidu.browser.isGecko = /gecko/i.test(navigator.userAgent) && !/like gecko/i.test(navigator.userAgent);
|
|
4447
|
+
|
|
4448
|
+
/**
|
|
4449
|
+
* 判断是否严格标准的渲染模式
|
|
4450
|
+
* @property isStrict
|
|
4451
|
+
* @grammar baidu.browser.isStrict
|
|
4452
|
+
* @meta standard
|
|
4453
|
+
*/
|
|
4454
|
+
baidu.browser.isStrict = document.compatMode == "CSS1Compat";
|
|
4455
|
+
|
|
4456
|
+
/**
|
|
4457
|
+
* 获取目标元素相对于整个文档左上角的位置
|
|
4458
|
+
* @name baidu.dom.getPosition
|
|
4459
|
+
* @function
|
|
4460
|
+
* @grammar baidu.dom.getPosition(element)
|
|
4461
|
+
* @param {HTMLElement|string} element 目标元素或目标元素的id
|
|
4462
|
+
* @meta standard
|
|
4463
|
+
*
|
|
4464
|
+
* @returns {Object} 目标元素的位置,键值为top和left的Object。
|
|
4465
|
+
*/
|
|
4466
|
+
baidu.dom.getPosition = function (element) {
|
|
4467
|
+
element = baidu.dom.g(element);
|
|
4468
|
+
var doc = baidu.dom.getDocument(element),
|
|
4469
|
+
browser = baidu.browser,
|
|
4470
|
+
getStyle = baidu.dom.getStyle;
|
|
4471
|
+
// Gecko 1.9版本以下用getBoxObjectFor计算位置
|
|
4472
|
+
// 但是某些情况下是有bug的
|
|
4473
|
+
// 对于这些有bug的情况
|
|
4474
|
+
// 使用递归查找的方式
|
|
4475
|
+
browser.isGecko > 0 &&
|
|
4476
|
+
doc.getBoxObjectFor &&
|
|
4477
|
+
getStyle(element, 'position') == 'absolute' &&
|
|
4478
|
+
(element.style.top === '' || element.style.left === '');
|
|
4479
|
+
var pos = {"left":0,"top":0},
|
|
4480
|
+
viewport = (browser.ie && !browser.isStrict) ? doc.body : doc.documentElement,
|
|
4481
|
+
parent,
|
|
4482
|
+
box;
|
|
4483
|
+
|
|
4484
|
+
if(element == viewport){
|
|
4485
|
+
return pos;
|
|
4486
|
+
}
|
|
4487
|
+
|
|
4488
|
+
if(element.getBoundingClientRect){ // IE and Gecko 1.9+
|
|
4489
|
+
|
|
4490
|
+
//当HTML或者BODY有border width时, 原生的getBoundingClientRect返回值是不符合预期的
|
|
4491
|
+
//考虑到通常情况下 HTML和BODY的border只会设成0px,所以忽略该问题.
|
|
4492
|
+
box = element.getBoundingClientRect();
|
|
4493
|
+
|
|
4494
|
+
pos.left = Math.floor(box.left) + Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft);
|
|
4495
|
+
pos.top = Math.floor(box.top) + Math.max(doc.documentElement.scrollTop, doc.body.scrollTop);
|
|
4496
|
+
|
|
4497
|
+
// IE会给HTML元素添加一个border,默认是medium(2px)
|
|
4498
|
+
// 但是在IE 6 7 的怪异模式下,可以被html { border: 0; } 这条css规则覆盖
|
|
4499
|
+
// 在IE7的标准模式下,border永远是2px,这个值通过clientLeft 和 clientTop取得
|
|
4500
|
+
// 但是。。。在IE 6 7的怪异模式,如果用户使用css覆盖了默认的medium
|
|
4501
|
+
// clientTop和clientLeft不会更新
|
|
4502
|
+
pos.left -= doc.documentElement.clientLeft;
|
|
4503
|
+
pos.top -= doc.documentElement.clientTop;
|
|
4504
|
+
|
|
4505
|
+
var htmlDom = doc.body,
|
|
4506
|
+
// 在这里,不使用element.style.borderLeftWidth,只有computedStyle是可信的
|
|
4507
|
+
htmlBorderLeftWidth = parseInt(getStyle(htmlDom, 'borderLeftWidth')),
|
|
4508
|
+
htmlBorderTopWidth = parseInt(getStyle(htmlDom, 'borderTopWidth'));
|
|
4509
|
+
if(browser.ie && !browser.isStrict){
|
|
4510
|
+
pos.left -= isNaN(htmlBorderLeftWidth) ? 2 : htmlBorderLeftWidth;
|
|
4511
|
+
pos.top -= isNaN(htmlBorderTopWidth) ? 2 : htmlBorderTopWidth;
|
|
4512
|
+
}
|
|
4513
|
+
} else {
|
|
4514
|
+
// safari/opera/firefox
|
|
4515
|
+
parent = element;
|
|
4516
|
+
|
|
4517
|
+
do {
|
|
4518
|
+
pos.left += parent.offsetLeft;
|
|
4519
|
+
pos.top += parent.offsetTop;
|
|
4520
|
+
|
|
4521
|
+
// safari里面,如果遍历到了一个fixed的元素,后面的offset都不准了
|
|
4522
|
+
if (browser.isWebkit > 0 && getStyle(parent, 'position') == 'fixed') {
|
|
4523
|
+
pos.left += doc.body.scrollLeft;
|
|
4524
|
+
pos.top += doc.body.scrollTop;
|
|
4525
|
+
break;
|
|
4526
|
+
}
|
|
4527
|
+
|
|
4528
|
+
parent = parent.offsetParent;
|
|
4529
|
+
} while (parent && parent != element);
|
|
4530
|
+
|
|
4531
|
+
// 对body offsetTop的修正
|
|
4532
|
+
if(browser.opera > 0 || (browser.isWebkit > 0 && getStyle(element, 'position') == 'absolute')){
|
|
4533
|
+
pos.top -= doc.body.offsetTop;
|
|
4534
|
+
}
|
|
4535
|
+
|
|
4536
|
+
// 计算除了body的scroll
|
|
4537
|
+
parent = element.offsetParent;
|
|
4538
|
+
while (parent && parent != doc.body) {
|
|
4539
|
+
pos.left -= parent.scrollLeft;
|
|
4540
|
+
// see https://bugs.opera.com/show_bug.cgi?id=249965
|
|
4541
|
+
if (!browser.opera || parent.tagName != 'TR') {
|
|
4542
|
+
pos.top -= parent.scrollTop;
|
|
4543
|
+
}
|
|
4544
|
+
parent = parent.offsetParent;
|
|
4545
|
+
}
|
|
4546
|
+
}
|
|
4547
|
+
|
|
4548
|
+
return pos;
|
|
4549
|
+
};
|
|
4550
|
+
|
|
4551
|
+
/**
|
|
4552
|
+
* @ignore
|
|
4553
|
+
* @namespace baidu.event 屏蔽浏览器差异性的事件封装。
|
|
4554
|
+
* @property target 事件的触发元素
|
|
4555
|
+
* @property pageX 鼠标事件的鼠标x坐标
|
|
4556
|
+
* @property pageY 鼠标事件的鼠标y坐标
|
|
4557
|
+
* @property keyCode 键盘事件的键值
|
|
4558
|
+
*/
|
|
4559
|
+
baidu.event = baidu.event || {};
|
|
4560
|
+
|
|
4561
|
+
/**
|
|
4562
|
+
* 事件监听器的存储表
|
|
4563
|
+
* @private
|
|
4564
|
+
* @meta standard
|
|
4565
|
+
*/
|
|
4566
|
+
baidu.event._listeners = baidu.event._listeners || [];
|
|
4567
|
+
|
|
4568
|
+
/**
|
|
4569
|
+
* 为目标元素添加事件监听器
|
|
4570
|
+
* @name baidu.event.on
|
|
4571
|
+
* @function
|
|
4572
|
+
* @grammar baidu.event.on(element, type, listener)
|
|
4573
|
+
* @param {HTMLElement|string|window} element 目标元素或目标元素id
|
|
4574
|
+
* @param {string} type 事件类型
|
|
4575
|
+
* @param {Function} listener 需要添加的监听器
|
|
4576
|
+
* @remark
|
|
4577
|
+
*
|
|
4578
|
+
1. 不支持跨浏览器的鼠标滚轮事件监听器添加<br>
|
|
4579
|
+
2. 改方法不为监听器灌入事件对象,以防止跨iframe事件挂载的事件对象获取失败
|
|
4580
|
+
|
|
4581
|
+
* @shortcut on
|
|
4582
|
+
* @meta standard
|
|
4583
|
+
* @see baidu.event.un
|
|
4584
|
+
*
|
|
4585
|
+
* @returns {HTMLElement|window} 目标元素
|
|
4586
|
+
*/
|
|
4587
|
+
baidu.event.on = function (element, type, listener) {
|
|
4588
|
+
type = type.replace(/^on/i, '');
|
|
4589
|
+
element = baidu.dom._g(element);
|
|
4590
|
+
|
|
4591
|
+
var realListener = function (ev) {
|
|
4592
|
+
// 1. 这里不支持EventArgument, 原因是跨frame的事件挂载
|
|
4593
|
+
// 2. element是为了修正this
|
|
4594
|
+
listener.call(element, ev);
|
|
4595
|
+
},
|
|
4596
|
+
lis = baidu.event._listeners,
|
|
4597
|
+
filter = baidu.event._eventFilter,
|
|
4598
|
+
afterFilter,
|
|
4599
|
+
realType = type;
|
|
4600
|
+
type = type.toLowerCase();
|
|
4601
|
+
// filter过滤
|
|
4602
|
+
if(filter && filter[type]){
|
|
4603
|
+
afterFilter = filter[type](element, type, realListener);
|
|
4604
|
+
realType = afterFilter.type;
|
|
4605
|
+
realListener = afterFilter.listener;
|
|
4606
|
+
}
|
|
4607
|
+
|
|
4608
|
+
// 事件监听器挂载
|
|
4609
|
+
if (element.addEventListener) {
|
|
4610
|
+
element.addEventListener(realType, realListener, false);
|
|
4611
|
+
} else if (element.attachEvent) {
|
|
4612
|
+
element.attachEvent('on' + realType, realListener);
|
|
4613
|
+
}
|
|
4614
|
+
|
|
4615
|
+
// 将监听器存储到数组中
|
|
4616
|
+
lis[lis.length] = [element, type, listener, realListener, realType];
|
|
4617
|
+
return element;
|
|
4618
|
+
};
|
|
4619
|
+
|
|
4620
|
+
// 声明快捷方法
|
|
4621
|
+
baidu.on = baidu.event.on;
|
|
4622
|
+
|
|
4623
|
+
/**
|
|
4624
|
+
* 返回一个当前页面的唯一标识字符串。
|
|
4625
|
+
* @name baidu.lang.guid
|
|
4626
|
+
* @function
|
|
4627
|
+
* @grammar baidu.lang.guid()
|
|
4628
|
+
* @version 1.1.1
|
|
4629
|
+
* @meta standard
|
|
4630
|
+
*
|
|
4631
|
+
* @returns {String} 当前页面的唯一标识字符串
|
|
4632
|
+
*/
|
|
4633
|
+
|
|
4634
|
+
(function(){
|
|
4635
|
+
//不直接使用window,可以提高3倍左右性能
|
|
4636
|
+
var guid = context[baidu.guid];
|
|
4637
|
+
|
|
4638
|
+
baidu.lang.guid = function() {
|
|
4639
|
+
return "TANGRAM__" + (guid._counter ++).toString(36);
|
|
4640
|
+
};
|
|
4641
|
+
|
|
4642
|
+
guid._counter = guid._counter || 1;
|
|
4643
|
+
})();
|
|
4644
|
+
|
|
4645
|
+
/**
|
|
4646
|
+
* 所有类的实例的容器
|
|
4647
|
+
* key为每个实例的guid
|
|
4648
|
+
* @meta standard
|
|
4649
|
+
*/
|
|
4650
|
+
|
|
4651
|
+
context[baidu.guid]._instances = context[baidu.guid]._instances || {};
|
|
4652
|
+
|
|
4653
|
+
/**
|
|
4654
|
+
* 判断目标参数是否为function或Function实例
|
|
4655
|
+
* @name baidu.lang.isFunction
|
|
4656
|
+
* @function
|
|
4657
|
+
* @grammar baidu.lang.isFunction(source)
|
|
4658
|
+
* @param {Any} source 目标参数
|
|
4659
|
+
* @version 1.2
|
|
4660
|
+
* @see baidu.lang.isString,baidu.lang.isObject,baidu.lang.isNumber,baidu.lang.isArray,baidu.lang.isElement,baidu.lang.isBoolean,baidu.lang.isDate
|
|
4661
|
+
* @meta standard
|
|
4662
|
+
* @returns {boolean} 类型判断结果
|
|
4663
|
+
*/
|
|
4664
|
+
baidu.lang.isFunction = function (source) {
|
|
4665
|
+
// chrome下,'function' == typeof /a/ 为true.
|
|
4666
|
+
return '[object Function]' == Object.prototype.toString.call(source);
|
|
4667
|
+
};
|
|
4668
|
+
|
|
4669
|
+
/**
|
|
4670
|
+
*
|
|
4671
|
+
* @ignore
|
|
4672
|
+
* @class Tangram继承机制提供的一个基类,用户可以通过继承baidu.lang.Class来获取它的属性及方法。
|
|
4673
|
+
* @name baidu.lang.Class
|
|
4674
|
+
* @grammar baidu.lang.Class(guid)
|
|
4675
|
+
* @param {string} guid 对象的唯一标识
|
|
4676
|
+
* @meta standard
|
|
4677
|
+
* @remark baidu.lang.Class和它的子类的实例均包含一个全局唯一的标识guid。guid是在构造函数中生成的,因此,继承自baidu.lang.Class的类应该直接或者间接调用它的构造函数。<br>baidu.lang.Class的构造函数中产生guid的方式可以保证guid的唯一性,及每个实例都有一个全局唯一的guid。
|
|
4678
|
+
* @meta standard
|
|
4679
|
+
* @see baidu.lang.inherits,baidu.lang.Event
|
|
4680
|
+
*/
|
|
4681
|
+
baidu.lang.Class = function(guid) {
|
|
4682
|
+
this.guid = guid || baidu.lang.guid();
|
|
4683
|
+
context[baidu.guid]._instances[this.guid] = this;
|
|
4684
|
+
};
|
|
4685
|
+
context[baidu.guid]._instances = context[baidu.guid]._instances || {};
|
|
4686
|
+
|
|
4687
|
+
/**
|
|
4688
|
+
* 释放对象所持有的资源,主要是自定义事件。
|
|
4689
|
+
* @name dispose
|
|
4690
|
+
* @grammar obj.dispose()
|
|
4691
|
+
*/
|
|
4692
|
+
baidu.lang.Class.prototype.dispose = function(){
|
|
4693
|
+
delete context[baidu.guid]._instances[this.guid];
|
|
4694
|
+
|
|
4695
|
+
for(var property in this){
|
|
4696
|
+
if (!baidu.lang.isFunction(this[property])) {
|
|
4697
|
+
delete this[property];
|
|
4698
|
+
}
|
|
4699
|
+
}
|
|
4700
|
+
this.disposed = true;
|
|
4701
|
+
};
|
|
4702
|
+
|
|
4703
|
+
/**
|
|
4704
|
+
* 重载了默认的toString方法,使得返回信息更加准确一些。
|
|
4705
|
+
* @return {string} 对象的String表示形式
|
|
4706
|
+
*/
|
|
4707
|
+
baidu.lang.Class.prototype.toString = function(){
|
|
4708
|
+
return "[object " + (this._className || "Object" ) + "]";
|
|
4709
|
+
};
|
|
4710
|
+
|
|
4711
|
+
/**
|
|
4712
|
+
* @ignore
|
|
4713
|
+
* @class 自定义的事件对象。
|
|
4714
|
+
* @name baidu.lang.Event
|
|
4715
|
+
* @grammar baidu.lang.Event(type[, target])
|
|
4716
|
+
* @param {string} type 事件类型名称。为了方便区分事件和一个普通的方法,事件类型名称必须以"on"(小写)开头。
|
|
4717
|
+
* @param {Object} [target]触发事件的对象
|
|
4718
|
+
* @meta standard
|
|
4719
|
+
* @remark 引入该模块,会自动为Class引入3个事件扩展方法:addEventListener、removeEventListener和dispatchEvent。
|
|
4720
|
+
* @meta standard
|
|
4721
|
+
* @see baidu.lang.Class
|
|
4722
|
+
*/
|
|
4723
|
+
baidu.lang.Event = function (type, target) {
|
|
4724
|
+
this.type = type;
|
|
4725
|
+
this.returnValue = true;
|
|
4726
|
+
this.target = target || null;
|
|
4727
|
+
this.currentTarget = null;
|
|
4728
|
+
};
|
|
4729
|
+
|
|
4730
|
+
/**
|
|
4731
|
+
* 注册对象的事件监听器。引入baidu.lang.Event后,Class的子类实例才会获得该方法。
|
|
4732
|
+
* @grammar obj.addEventListener(type, handler[, key])
|
|
4733
|
+
* @param {string} type 自定义事件的名称
|
|
4734
|
+
* @param {Function} handler 自定义事件被触发时应该调用的回调函数
|
|
4735
|
+
* @param {string} [key] 为事件监听函数指定的名称,可在移除时使用。如果不提供,方法会默认为它生成一个全局唯一的key。
|
|
4736
|
+
* @remark 事件类型区分大小写。如果自定义事件名称不是以小写"on"开头,该方法会给它加上"on"再进行判断,即"click"和"onclick"会被认为是同一种事件。
|
|
4737
|
+
*/
|
|
4738
|
+
baidu.lang.Class.prototype.addEventListener = function (type, handler, key) {
|
|
4739
|
+
if (!baidu.lang.isFunction(handler)) {
|
|
4740
|
+
return;
|
|
4741
|
+
}
|
|
4742
|
+
|
|
4743
|
+
!this.__listeners && (this.__listeners = {});
|
|
4744
|
+
|
|
4745
|
+
var t = this.__listeners, id;
|
|
4746
|
+
if (typeof key == "string" && key) {
|
|
4747
|
+
if (/[^\w\-]/.test(key)) {
|
|
4748
|
+
throw("nonstandard key:" + key);
|
|
4749
|
+
} else {
|
|
4750
|
+
handler.hashCode = key;
|
|
4751
|
+
id = key;
|
|
4752
|
+
}
|
|
4753
|
+
}
|
|
4754
|
+
type.indexOf("on") != 0 && (type = "on" + type);
|
|
4755
|
+
|
|
4756
|
+
typeof t[type] != "object" && (t[type] = {});
|
|
4757
|
+
id = id || baidu.lang.guid();
|
|
4758
|
+
handler.hashCode = id;
|
|
4759
|
+
t[type][id] = handler;
|
|
4760
|
+
};
|
|
4761
|
+
|
|
4762
|
+
/**
|
|
4763
|
+
* 移除对象的事件监听器。引入baidu.lang.Event后,Class的子类实例才会获得该方法。
|
|
4764
|
+
* @grammar obj.removeEventListener(type, handler)
|
|
4765
|
+
* @param {string} type 事件类型
|
|
4766
|
+
* @param {Function|string} handler 要移除的事件监听函数或者监听函数的key
|
|
4767
|
+
* @remark 如果第二个参数handler没有被绑定到对应的自定义事件中,什么也不做。
|
|
4768
|
+
*/
|
|
4769
|
+
baidu.lang.Class.prototype.removeEventListener = function (type, handler) {
|
|
4770
|
+
if (typeof handler != "undefined") {
|
|
4771
|
+
if ( (baidu.lang.isFunction(handler) && ! (handler = handler.hashCode))
|
|
4772
|
+
|| (! baidu.lang.isString(handler))
|
|
4773
|
+
){
|
|
4774
|
+
return;
|
|
4775
|
+
}
|
|
4776
|
+
}
|
|
4777
|
+
|
|
4778
|
+
!this.__listeners && (this.__listeners = {});
|
|
4779
|
+
|
|
4780
|
+
type.indexOf("on") != 0 && (type = "on" + type);
|
|
4781
|
+
|
|
4782
|
+
var t = this.__listeners;
|
|
4783
|
+
if (!t[type]) {
|
|
4784
|
+
return;
|
|
4785
|
+
}
|
|
4786
|
+
if (typeof handler != "undefined") {
|
|
4787
|
+
t[type][handler] && delete t[type][handler];
|
|
4788
|
+
} else {
|
|
4789
|
+
for(var guid in t[type]){
|
|
4790
|
+
delete t[type][guid];
|
|
4791
|
+
}
|
|
4792
|
+
}
|
|
4793
|
+
};
|
|
4794
|
+
|
|
4795
|
+
/**
|
|
4796
|
+
* 派发自定义事件,使得绑定到自定义事件上面的函数都会被执行。引入baidu.lang.Event后,Class的子类实例才会获得该方法。
|
|
4797
|
+
* @grammar obj.dispatchEvent(event, options)
|
|
4798
|
+
* @param {baidu.lang.Event|String} event Event对象,或事件名称(1.1.1起支持)
|
|
4799
|
+
* @param {Object} options 扩展参数,所含属性键值会扩展到Event对象上(1.2起支持)
|
|
4800
|
+
* @remark 处理会调用通过addEventListenr绑定的自定义事件回调函数之外,还会调用直接绑定到对象上面的自定义事件。例如:<br>
|
|
4801
|
+
myobj.onMyEvent = function(){}<br>
|
|
4802
|
+
myobj.addEventListener("onMyEvent", function(){});
|
|
4803
|
+
*/
|
|
4804
|
+
baidu.lang.Class.prototype.dispatchEvent = function (event, options) {
|
|
4805
|
+
if (baidu.lang.isString(event)) {
|
|
4806
|
+
event = new baidu.lang.Event(event);
|
|
4807
|
+
}
|
|
4808
|
+
!this.__listeners && (this.__listeners = {});
|
|
4809
|
+
|
|
4810
|
+
// 20100603 添加本方法的第二个参数,将 options extend到event中去传递
|
|
4811
|
+
options = options || {};
|
|
4812
|
+
for (var i in options) {
|
|
4813
|
+
event[i] = options[i];
|
|
4814
|
+
}
|
|
4815
|
+
|
|
4816
|
+
var i, t = this.__listeners, p = event.type;
|
|
4817
|
+
event.target = event.target || this;
|
|
4818
|
+
event.currentTarget = this;
|
|
4819
|
+
|
|
4820
|
+
p.indexOf("on") != 0 && (p = "on" + p);
|
|
4821
|
+
|
|
4822
|
+
baidu.lang.isFunction(this[p]) && this[p].apply(this, arguments);
|
|
4823
|
+
|
|
4824
|
+
if (typeof t[p] == "object") {
|
|
4825
|
+
for (i in t[p]) {
|
|
4826
|
+
t[p][i].apply(this, arguments);
|
|
4827
|
+
}
|
|
4828
|
+
}
|
|
4829
|
+
return event.returnValue;
|
|
4830
|
+
};
|
|
4831
|
+
|
|
4832
|
+
|
|
4833
|
+
baidu.lang.inherits = function (subClass, superClass, className) {
|
|
4834
|
+
var key, proto,
|
|
4835
|
+
selfProps = subClass.prototype,
|
|
4836
|
+
clazz = new Function();
|
|
4837
|
+
|
|
4838
|
+
clazz.prototype = superClass.prototype;
|
|
4839
|
+
proto = subClass.prototype = new clazz();
|
|
4840
|
+
for (key in selfProps) {
|
|
4841
|
+
proto[key] = selfProps[key];
|
|
4842
|
+
}
|
|
4843
|
+
subClass.prototype.constructor = subClass;
|
|
4844
|
+
subClass.superClass = superClass.prototype;
|
|
4845
|
+
|
|
4846
|
+
// 类名标识,兼容Class的toString,基本没用
|
|
4847
|
+
if ("string" == typeof className) {
|
|
4848
|
+
proto._className = className;
|
|
4849
|
+
}
|
|
4850
|
+
};
|
|
4851
|
+
// 声明快捷方法
|
|
4852
|
+
baidu.inherits = baidu.lang.inherits;
|
|
4853
|
+
|
|
4854
|
+
|
|
4855
|
+
/**
|
|
4856
|
+
|
|
4857
|
+
* 图片的路径
|
|
4858
|
+
|
|
4859
|
+
* @private
|
|
4860
|
+
* @type {String}
|
|
4861
|
+
|
|
4862
|
+
*/
|
|
4863
|
+
var _IMAGE_PATH = 'http://api.map.baidu.com/library/TextIconOverlay/1.2/src/images/m';
|
|
4864
|
+
|
|
4865
|
+
/**
|
|
4866
|
+
|
|
4867
|
+
* 图片的后缀名
|
|
4868
|
+
|
|
4869
|
+
* @private
|
|
4870
|
+
* @type {String}
|
|
4871
|
+
|
|
4872
|
+
*/
|
|
4873
|
+
var _IMAGE_EXTENSION = 'png';
|
|
4874
|
+
|
|
4875
|
+
/**
|
|
4876
|
+
*@exports TextIconOverlay as BMapLib.TextIconOverlay
|
|
4877
|
+
*/
|
|
4878
|
+
|
|
4879
|
+
/**
|
|
4880
|
+
* TextIconOverlay
|
|
4881
|
+
* @class 此类表示地图上的一个覆盖物,该覆盖物由文字和图标组成,从Overlay继承。文字通常是数字(0-9)或字母(A-Z ),而文字与图标之间有一定的映射关系。
|
|
4882
|
+
* 该覆盖物适用于以下类似的场景:需要在地图上添加一系列覆盖物,这些覆盖物之间用不同的图标和文字来区分,文字可能表示了该覆盖物的某一属性值,根据该文字和一定的映射关系,自动匹配相应颜色和大小的图标。
|
|
4883
|
+
*
|
|
4884
|
+
* @constructor
|
|
4885
|
+
* @param {Point} position 表示一个经纬度坐标位置。
|
|
4886
|
+
* @param {string} text 表示该覆盖物显示的文字信息。
|
|
4887
|
+
* @param {TextIconOverlayOptions=} options 选项。
|
|
4888
|
+
*
|
|
4889
|
+
* @typedef {object} TextIconOverlayOptions
|
|
4890
|
+
* @property {IconStyle[]=} styles 一组图标风格。
|
|
4891
|
+
* @property {number=} zIndex 等同与CSS中的z-index属性
|
|
4892
|
+
*
|
|
4893
|
+
* @typedef {object} IconStyle
|
|
4894
|
+
* @property {string} url 图片的url地址。
|
|
4895
|
+
* @property {Size} size 图片的大小。
|
|
4896
|
+
* @property {Size=} anchor 图标定位在地图上的位置相对于图标左上角的偏移值,默认偏移值为图标的中心位置。
|
|
4897
|
+
* @property {Size=} offset 图片相对于可视区域的偏移值,此功能的作用等同于CSS中的background-position属性。
|
|
4898
|
+
* @property {number=} [textSize=10] 文字的大小。
|
|
4899
|
+
* @property {string} [textColor='black'] 文字的颜色。
|
|
4900
|
+
*/
|
|
4901
|
+
var TextIconOverlay = function(position, text, options){
|
|
4902
|
+
try {
|
|
4903
|
+
BMap;
|
|
4904
|
+
} catch (e) {
|
|
4905
|
+
throw Error('Baidu Map JS API is not ready yet!');
|
|
4906
|
+
}
|
|
4907
|
+
T.lang.inherits(TextIconOverlay, BMap.Overlay, "TextIconOverlay");
|
|
4908
|
+
this._position = position;
|
|
4909
|
+
this._text = text;
|
|
4910
|
+
this._options = options || {};
|
|
4911
|
+
this._styles = this._options['styles'] || [];
|
|
4912
|
+
/** @type {number | undefined} */
|
|
4913
|
+
this._zIndex = this._options['zIndex'];
|
|
4914
|
+
(!this._styles.length) && this._setupDefaultStyles();
|
|
4915
|
+
};
|
|
4916
|
+
|
|
4917
|
+
|
|
4918
|
+
TextIconOverlay.prototype._setupDefaultStyles = function(){
|
|
4919
|
+
var sizes = [53, 56, 66, 78, 90];
|
|
4920
|
+
for(var i = 0, size; size = sizes[i]; i++){
|
|
4921
|
+
this._styles.push({
|
|
4922
|
+
url:_IMAGE_PATH + i + '.' + _IMAGE_EXTENSION,
|
|
4923
|
+
size: new BMap.Size(size, size)
|
|
4924
|
+
});
|
|
4925
|
+
}//for循环的简洁写法
|
|
4926
|
+
};
|
|
4927
|
+
|
|
4928
|
+
/**
|
|
4929
|
+
*继承Overlay的intialize方法,自定义覆盖物时必须。
|
|
4930
|
+
*@param {Map} map BMap.Map的实例化对象。
|
|
4931
|
+
*@return {HTMLElement} 返回覆盖物对应的HTML元素。
|
|
4932
|
+
*/
|
|
4933
|
+
TextIconOverlay.prototype.initialize = function(map){
|
|
4934
|
+
this._map = map;
|
|
4935
|
+
|
|
4936
|
+
this._domElement = document.createElement('div');
|
|
4937
|
+
this._updateCss();
|
|
4938
|
+
this._updateText();
|
|
4939
|
+
this._updatePosition();
|
|
4940
|
+
|
|
4941
|
+
this._bind();
|
|
4942
|
+
|
|
4943
|
+
this._map.getPanes().markerMouseTarget.appendChild(this._domElement);
|
|
4944
|
+
return this._domElement;
|
|
4945
|
+
};
|
|
4946
|
+
|
|
4947
|
+
/**
|
|
4948
|
+
*继承Overlay的draw方法,自定义覆盖物时必须。
|
|
4949
|
+
*@return 无返回值。
|
|
4950
|
+
*/
|
|
4951
|
+
TextIconOverlay.prototype.draw = function(){
|
|
4952
|
+
this._map && this._updatePosition();
|
|
4953
|
+
};
|
|
4954
|
+
|
|
4955
|
+
/**
|
|
4956
|
+
*获取该覆盖物上的文字。
|
|
4957
|
+
*@return {String} 该覆盖物上的文字。
|
|
4958
|
+
*/
|
|
4959
|
+
TextIconOverlay.prototype.getText = function(){
|
|
4960
|
+
return this._text;
|
|
4961
|
+
};
|
|
4962
|
+
|
|
4963
|
+
/**
|
|
4964
|
+
*设置该覆盖物上的文字。
|
|
4965
|
+
*@param {String} text 要设置的文字,通常是字母A-Z或数字0-9。
|
|
4966
|
+
*@return 无返回值。
|
|
4967
|
+
*/
|
|
4968
|
+
TextIconOverlay.prototype.setText = function(text){
|
|
4969
|
+
if(text && (!this._text || (this._text.toString() != text.toString()))){
|
|
4970
|
+
this._text = text;
|
|
4971
|
+
this._updateText();
|
|
4972
|
+
this._updateCss();
|
|
4973
|
+
this._updatePosition();
|
|
4974
|
+
}
|
|
4975
|
+
};
|
|
4976
|
+
|
|
4977
|
+
/**
|
|
4978
|
+
*获取该覆盖物的位置。
|
|
4979
|
+
*@return {Point} 该覆盖物的经纬度坐标。
|
|
4980
|
+
*/
|
|
4981
|
+
TextIconOverlay.prototype.getPosition = function () {
|
|
4982
|
+
return this._position;
|
|
4983
|
+
};
|
|
4984
|
+
|
|
4985
|
+
/**
|
|
4986
|
+
*设置该覆盖物的位置。
|
|
4987
|
+
*@param {Point} position 要设置的经纬度坐标。
|
|
4988
|
+
*@return 无返回值。
|
|
4989
|
+
*/
|
|
4990
|
+
TextIconOverlay.prototype.setPosition = function (position) {
|
|
4991
|
+
if(position && (!this._position || !this._position.equals(position))){
|
|
4992
|
+
this._position = position;
|
|
4993
|
+
this._updatePosition();
|
|
4994
|
+
}
|
|
4995
|
+
};
|
|
4996
|
+
|
|
4997
|
+
/**
|
|
4998
|
+
*由文字信息获取风格数组的对应索引值。
|
|
4999
|
+
*内部默认的对应函数为文字转换为数字除以10的结果,比如文字8返回索引0,文字25返回索引2.
|
|
5000
|
+
*如果需要自定义映射关系,请覆盖该函数。
|
|
5001
|
+
*@param {String} text 文字。
|
|
5002
|
+
*@param {Array<IconStyle>} styles 一组图标风格。
|
|
5003
|
+
*@return {Number} 对应的索引值。
|
|
5004
|
+
*/
|
|
5005
|
+
TextIconOverlay.prototype.getStyleByText = function(text, styles){
|
|
5006
|
+
var count = parseInt(text);
|
|
5007
|
+
var index = parseInt(count / 10);
|
|
5008
|
+
index = Math.max(0, index);
|
|
5009
|
+
index = Math.min(index, styles.length - 1);
|
|
5010
|
+
return styles[index];
|
|
5011
|
+
};
|
|
5012
|
+
|
|
5013
|
+
/**
|
|
5014
|
+
*更新相应的CSS。
|
|
5015
|
+
*@return 无返回值。
|
|
5016
|
+
*/
|
|
5017
|
+
TextIconOverlay.prototype._updateCss = function(){
|
|
5018
|
+
if (!this._domElement) {
|
|
5019
|
+
return
|
|
5020
|
+
}
|
|
5021
|
+
var style = this.getStyleByText(this._text, this._styles);
|
|
5022
|
+
this._domElement.style.cssText = this._buildCssText(style);
|
|
5023
|
+
};
|
|
5024
|
+
|
|
5025
|
+
/**
|
|
5026
|
+
*更新覆盖物的显示文字。
|
|
5027
|
+
*@return 无返回值。
|
|
5028
|
+
*/
|
|
5029
|
+
TextIconOverlay.prototype._updateText = function(){
|
|
5030
|
+
if (this._domElement) {
|
|
5031
|
+
this._domElement.innerHTML = this._text;
|
|
5032
|
+
}
|
|
5033
|
+
};
|
|
5034
|
+
|
|
5035
|
+
/**
|
|
5036
|
+
*调整覆盖物在地图上的位置更新覆盖物的显示文字。
|
|
5037
|
+
*@return 无返回值。
|
|
5038
|
+
*/
|
|
5039
|
+
TextIconOverlay.prototype._updatePosition = function(){
|
|
5040
|
+
if (this._domElement && this._position) {
|
|
5041
|
+
var style = this._domElement.style;
|
|
5042
|
+
var pixelPosition= this._map.pointToOverlayPixel(this._position);
|
|
5043
|
+
pixelPosition.x -= Math.ceil(parseInt(style.width) / 2);
|
|
5044
|
+
pixelPosition.y -= Math.ceil(parseInt(style.height) / 2);
|
|
5045
|
+
style.left = pixelPosition.x + "px";
|
|
5046
|
+
style.top = pixelPosition.y + "px";
|
|
5047
|
+
}
|
|
5048
|
+
};
|
|
5049
|
+
|
|
5050
|
+
/**
|
|
5051
|
+
* 为该覆盖物的HTML元素构建CSS
|
|
5052
|
+
* @param {IconStyle} 一个图标的风格。
|
|
5053
|
+
* @return {String} 构建完成的CSSTEXT。
|
|
5054
|
+
*/
|
|
5055
|
+
TextIconOverlay.prototype._buildCssText = function(style) {
|
|
5056
|
+
//根据style来确定一些默认值
|
|
5057
|
+
var url = style['url'];
|
|
5058
|
+
var size = style['size'];
|
|
5059
|
+
var anchor = style['anchor'];
|
|
5060
|
+
var offset = style['offset'];
|
|
5061
|
+
var textColor = style['textColor'] || 'black';
|
|
5062
|
+
var textSize = style['textSize'] || 10;
|
|
5063
|
+
|
|
5064
|
+
var csstext = [];
|
|
5065
|
+
if (this._zIndex) {
|
|
5066
|
+
csstext.push('z-index:' + this._zIndex + ';');
|
|
5067
|
+
}
|
|
5068
|
+
if (T.browser["ie"] < 7) {
|
|
5069
|
+
csstext.push('filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(' +
|
|
5070
|
+
'sizingMethod=scale,src="' + url + '");');
|
|
5071
|
+
} else {
|
|
5072
|
+
csstext.push('background-image:url(' + url + ');');
|
|
5073
|
+
var backgroundPosition = '0 0';
|
|
5074
|
+
(offset instanceof BMap.Size) && (backgroundPosition = offset.width + 'px' + ' ' + offset.height + 'px');
|
|
5075
|
+
csstext.push('background-position:' + backgroundPosition + ';');
|
|
5076
|
+
}
|
|
5077
|
+
|
|
5078
|
+
if (size instanceof BMap.Size){
|
|
5079
|
+
if (anchor instanceof BMap.Size) {
|
|
5080
|
+
if (anchor.height > 0 && anchor.height < size.height) {
|
|
5081
|
+
csstext.push('height:' + (size.height - anchor.height) + 'px; padding-top:' + anchor.height + 'px;');
|
|
5082
|
+
}
|
|
5083
|
+
if(anchor.width > 0 && anchor.width < size.width){
|
|
5084
|
+
csstext.push('width:' + (size.width - anchor.width) + 'px; padding-left:' + anchor.width + 'px;');
|
|
5085
|
+
}
|
|
5086
|
+
} else {
|
|
5087
|
+
csstext.push('height:' + size.height + 'px; line-height:' + size.height + 'px;');
|
|
5088
|
+
csstext.push('width:' + size.width + 'px; text-align:center;');
|
|
5089
|
+
}
|
|
5090
|
+
}
|
|
5091
|
+
|
|
5092
|
+
csstext.push('cursor:pointer; color:' + textColor + '; position:absolute; font-size:' +
|
|
5093
|
+
textSize + 'px; font-family:Arial,sans-serif; font-weight:bold');
|
|
5094
|
+
return csstext.join('');
|
|
5095
|
+
};
|
|
5096
|
+
|
|
5097
|
+
|
|
5098
|
+
/**
|
|
5099
|
+
|
|
5100
|
+
* 当鼠标点击该覆盖物时会触发该事件
|
|
5101
|
+
|
|
5102
|
+
* @name TextIconOverlay#click
|
|
5103
|
+
|
|
5104
|
+
* @event
|
|
5105
|
+
|
|
5106
|
+
* @param {Event Object} e 回调函数会返回event参数,包括以下返回值:
|
|
5107
|
+
|
|
5108
|
+
* <br />"<b>type</b> : {String} 事件类型
|
|
5109
|
+
|
|
5110
|
+
* <br />"<b>target</b>:{BMapLib.TextIconOverlay} 事件目标
|
|
5111
|
+
|
|
5112
|
+
*
|
|
5113
|
+
|
|
5114
|
+
*/
|
|
5115
|
+
|
|
5116
|
+
/**
|
|
5117
|
+
|
|
5118
|
+
* 当鼠标进入该覆盖物区域时会触发该事件
|
|
5119
|
+
|
|
5120
|
+
* @name TextIconOverlay#mouseover
|
|
5121
|
+
|
|
5122
|
+
* @event
|
|
5123
|
+
* @param {Event Object} e 回调函数会返回event参数,包括以下返回值:
|
|
5124
|
+
|
|
5125
|
+
* <br />"<b>type</b> : {String} 事件类型
|
|
5126
|
+
|
|
5127
|
+
* <br />"<b>target</b>:{BMapLib.TextIconOverlay} 事件目标
|
|
5128
|
+
|
|
5129
|
+
* <br />"<b>point</b> : {BMap.Point} 最新添加上的节点BMap.Point对象
|
|
5130
|
+
|
|
5131
|
+
* <br />"<b>pixel</b>:{BMap.pixel} 最新添加上的节点BMap.Pixel对象
|
|
5132
|
+
|
|
5133
|
+
*
|
|
5134
|
+
|
|
5135
|
+
* @example <b>参考示例:</b><br />
|
|
5136
|
+
|
|
5137
|
+
* myTextIconOverlay.addEventListener("mouseover", function(e) { alert(e.point); });
|
|
5138
|
+
|
|
5139
|
+
*/
|
|
5140
|
+
|
|
5141
|
+
/**
|
|
5142
|
+
|
|
5143
|
+
* 当鼠标离开该覆盖物区域时会触发该事件
|
|
5144
|
+
|
|
5145
|
+
* @name TextIconOverlay#mouseout
|
|
5146
|
+
|
|
5147
|
+
* @event
|
|
5148
|
+
|
|
5149
|
+
* @param {Event Object} e 回调函数会返回event参数,包括以下返回值:
|
|
5150
|
+
|
|
5151
|
+
* <br />"<b>type</b> : {String} 事件类型
|
|
5152
|
+
|
|
5153
|
+
* <br />"<b>target</b>:{BMapLib.TextIconOverlay} 事件目标
|
|
5154
|
+
|
|
5155
|
+
* <br />"<b>point</b> : {BMap.Point} 最新添加上的节点BMap.Point对象
|
|
5156
|
+
|
|
5157
|
+
* <br />"<b>pixel</b>:{BMap.pixel} 最新添加上的节点BMap.Pixel对象
|
|
5158
|
+
|
|
5159
|
+
*
|
|
5160
|
+
|
|
5161
|
+
* @example <b>参考示例:</b><br />
|
|
5162
|
+
|
|
5163
|
+
* myTextIconOverlay.addEventListener("mouseout", function(e) { alert(e.point); });
|
|
5164
|
+
|
|
5165
|
+
*/
|
|
5166
|
+
|
|
5167
|
+
|
|
5168
|
+
/**
|
|
5169
|
+
* 为该覆盖物绑定一系列事件
|
|
5170
|
+
* 当前支持click mouseover mouseout
|
|
5171
|
+
* @return 无返回值。
|
|
5172
|
+
*/
|
|
5173
|
+
TextIconOverlay.prototype._bind = function(){
|
|
5174
|
+
if (!this._domElement){
|
|
5175
|
+
return;
|
|
5176
|
+
}
|
|
5177
|
+
|
|
5178
|
+
var me = this;
|
|
5179
|
+
var map = this._map;
|
|
5180
|
+
|
|
5181
|
+
var BaseEvent = T.lang.Event;
|
|
5182
|
+
function eventExtend(e, be){
|
|
5183
|
+
var elem = e.srcElement || e.target;
|
|
5184
|
+
var x = e.clientX || e.pageX;
|
|
5185
|
+
var y = e.clientY || e.pageY;
|
|
5186
|
+
if (e && be && x && y && elem){
|
|
5187
|
+
var offset = T.dom.getPosition(map.getContainer());
|
|
5188
|
+
be.pixel = new BMap.Pixel(x - offset.left, y - offset.top);
|
|
5189
|
+
be.point = map.pixelToPoint(be.pixel);
|
|
5190
|
+
}
|
|
5191
|
+
return be;
|
|
5192
|
+
}//给事件参数增加pixel和point两个值
|
|
5193
|
+
|
|
5194
|
+
T.event.on(this._domElement,"mouseover", function(e){
|
|
5195
|
+
me.dispatchEvent(eventExtend(e, new BaseEvent("onmouseover")));
|
|
5196
|
+
});
|
|
5197
|
+
T.event.on(this._domElement,"mouseout", function(e){
|
|
5198
|
+
me.dispatchEvent(eventExtend(e, new BaseEvent("onmouseout")));
|
|
5199
|
+
});
|
|
5200
|
+
T.event.on(this._domElement,"click", function(e){
|
|
5201
|
+
me.dispatchEvent(eventExtend(e, new BaseEvent("onclick")));
|
|
5202
|
+
});
|
|
5203
|
+
};
|
|
5204
|
+
|
|
5205
|
+
return TextIconOverlay;
|
|
5206
|
+
});
|
|
5207
|
+
} (bmaplib_texticonoverlay$1));
|
|
5208
|
+
return bmaplib_texticonoverlay$1.exports;
|
|
5209
|
+
}
|
|
5210
|
+
|
|
5211
|
+
var bmaplib_texticonoverlayExports = requireBmaplib_texticonoverlay();
|
|
5212
|
+
var TextIconOverlay = /*@__PURE__*/getDefaultExportFromCjs(bmaplib_texticonoverlayExports);
|
|
5213
|
+
|
|
5214
|
+
/**
|
|
5215
|
+
* 获取一个扩展的视图范围,把上下左右都扩大一样的像素值。
|
|
5216
|
+
* @param {BMap.Map} map BMap.Map的实例化对象
|
|
5217
|
+
* @param {BMap.Bounds} bounds BMap.Bounds的实例化对象
|
|
5218
|
+
* @param {Number} gridSize 要扩大的像素值
|
|
5219
|
+
*
|
|
5220
|
+
* @return {BMap.Bounds} 返回扩大后的视图范围。
|
|
5221
|
+
*/
|
|
5222
|
+
var getExtendedBounds = function(map, bounds, gridSize){
|
|
5223
|
+
bounds = cutBoundsInRange(bounds);
|
|
5224
|
+
var pixelNE = map.pointToPixel(bounds.getNorthEast());
|
|
5225
|
+
var pixelSW = map.pointToPixel(bounds.getSouthWest());
|
|
5226
|
+
pixelNE.x += gridSize;
|
|
5227
|
+
pixelNE.y -= gridSize;
|
|
5228
|
+
pixelSW.x -= gridSize;
|
|
5229
|
+
pixelSW.y += gridSize;
|
|
5230
|
+
var newNE = map.pixelToPoint(pixelNE);
|
|
5231
|
+
var newSW = map.pixelToPoint(pixelSW);
|
|
5232
|
+
return new BMap.Bounds(newSW, newNE);
|
|
5233
|
+
};
|
|
5234
|
+
|
|
5235
|
+
/**
|
|
5236
|
+
* 按照百度地图支持的世界范围对bounds进行边界处理
|
|
5237
|
+
* @param {BMap.Bounds} bounds BMap.Bounds的实例化对象
|
|
5238
|
+
*
|
|
5239
|
+
* @return {BMap.Bounds} 返回不越界的视图范围
|
|
5240
|
+
*/
|
|
5241
|
+
var cutBoundsInRange = function (bounds) {
|
|
5242
|
+
var maxX = getRange(bounds.getNorthEast().lng, -180, 180);
|
|
5243
|
+
var minX = getRange(bounds.getSouthWest().lng, -180, 180);
|
|
5244
|
+
var maxY = getRange(bounds.getNorthEast().lat, -74, 74);
|
|
5245
|
+
var minY = getRange(bounds.getSouthWest().lat, -74, 74);
|
|
5246
|
+
return new BMap.Bounds(new BMap.Point(minX, minY), new BMap.Point(maxX, maxY));
|
|
5247
|
+
};
|
|
5248
|
+
|
|
5249
|
+
/**
|
|
5250
|
+
* 对单个值进行边界处理。
|
|
5251
|
+
* @param {Number} i 要处理的数值
|
|
5252
|
+
* @param {Number} min 下边界值
|
|
5253
|
+
* @param {Number} max 上边界值
|
|
5254
|
+
*
|
|
5255
|
+
* @return {Number} 返回不越界的数值
|
|
5256
|
+
*/
|
|
5257
|
+
var getRange = function (i, mix, max) {
|
|
5258
|
+
mix && (i = Math.max(i, mix));
|
|
5259
|
+
max && (i = Math.min(i, max));
|
|
5260
|
+
return i;
|
|
5261
|
+
};
|
|
5262
|
+
|
|
5263
|
+
/**
|
|
5264
|
+
* 判断给定的对象是否为数组
|
|
5265
|
+
* @param {Object} source 要测试的对象
|
|
5266
|
+
*
|
|
5267
|
+
* @return {Boolean} 如果是数组返回true,否则返回false
|
|
5268
|
+
*/
|
|
5269
|
+
var isArray = function (source) {
|
|
5270
|
+
return '[object Array]' === Object.prototype.toString.call(source);
|
|
5271
|
+
};
|
|
5272
|
+
|
|
5273
|
+
/**
|
|
5274
|
+
* 返回item在source中的索引位置
|
|
5275
|
+
* @param {Object} item 要测试的对象
|
|
5276
|
+
* @param {Array} source 数组
|
|
5277
|
+
*
|
|
5278
|
+
* @return {Number} 如果在数组内,返回索引,否则返回-1
|
|
5279
|
+
*/
|
|
5280
|
+
var indexOf = function(item, source){
|
|
5281
|
+
var index = -1;
|
|
5282
|
+
if(isArray(source)){
|
|
5283
|
+
if (source.indexOf) {
|
|
5284
|
+
index = source.indexOf(item);
|
|
5285
|
+
} else {
|
|
5286
|
+
for (var i = 0, m; m = source[i]; i++) {
|
|
5287
|
+
if (m === item) {
|
|
5288
|
+
index = i;
|
|
5289
|
+
break;
|
|
5290
|
+
}
|
|
5291
|
+
}
|
|
5292
|
+
}
|
|
5293
|
+
}
|
|
5294
|
+
return index;
|
|
5295
|
+
};
|
|
5296
|
+
|
|
5297
|
+
/**
|
|
5298
|
+
*@exports MarkerClusterer as BMapLib.MarkerClusterer
|
|
5299
|
+
*/
|
|
5300
|
+
|
|
5301
|
+
/**
|
|
5302
|
+
* MarkerClusterer
|
|
5303
|
+
* @class 用来解决加载大量点要素到地图上产生覆盖现象的问题,并提高性能
|
|
5304
|
+
* @constructor
|
|
5305
|
+
* @param {BMap.Map} map 地图的一个实例。
|
|
5306
|
+
* @param {MarkerClustererOptions=} options 可选参数
|
|
5307
|
+
*
|
|
5308
|
+
* @typedef {object} MarkerClustererOptions
|
|
5309
|
+
* @property {Marker[]=} markers 要聚合的标记数组
|
|
5310
|
+
* @property {number=} [gridSize=60] 聚合计算时网格的像素大小,默认60
|
|
5311
|
+
* @property {number=} maxZoom 最大的聚合级别,大于该级别就不进行相应的聚合
|
|
5312
|
+
* @property {number=} [minClusterSize=2] 最小的聚合数量,小于该数量的不能成为一个聚合,默认为2
|
|
5313
|
+
* @property {boolean=} [isAverageCenter=false] 聚合点的落脚位置是否是所有聚合在内点的平均值,默认为否,落脚在聚合内的第一个点
|
|
5314
|
+
* @property {IconStyle[]=} styles 自定义聚合后的图标风格,请参考TextIconOverlay类
|
|
5315
|
+
* @property {number=} zIndex 聚合图标的z-index
|
|
5316
|
+
*/
|
|
5317
|
+
var MarkerClusterer$1 = function(map, options){
|
|
5318
|
+
try {
|
|
5319
|
+
BMap;
|
|
5320
|
+
} catch (e) {
|
|
5321
|
+
throw Error('Baidu Map JS API is not ready yet!');
|
|
5322
|
+
}
|
|
5323
|
+
if (!map){
|
|
5324
|
+
return;
|
|
5325
|
+
}
|
|
5326
|
+
this._map = map;
|
|
5327
|
+
this._markers = [];
|
|
5328
|
+
this._clusters = [];
|
|
5329
|
+
|
|
5330
|
+
var opts = options || {};
|
|
5331
|
+
this._zIndex = opts["zIndex"];
|
|
5332
|
+
this._gridSize = opts["gridSize"] || 60;
|
|
5333
|
+
this._maxZoom = opts["maxZoom"] || 18;
|
|
5334
|
+
this._minClusterSize = opts["minClusterSize"] || 2;
|
|
5335
|
+
this._isAverageCenter = false;
|
|
5336
|
+
if (opts['isAverageCenter'] != undefined) {
|
|
5337
|
+
this._isAverageCenter = opts['isAverageCenter'];
|
|
5338
|
+
}
|
|
5339
|
+
this._styles = opts["styles"] || [];
|
|
5340
|
+
|
|
5341
|
+
var that = this;
|
|
5342
|
+
this._map.addEventListener("zoomend",function(){
|
|
5343
|
+
that._redraw();
|
|
5344
|
+
});
|
|
5345
|
+
|
|
5346
|
+
this._map.addEventListener("moveend",function(){
|
|
5347
|
+
that._redraw();
|
|
5348
|
+
});
|
|
5349
|
+
|
|
5350
|
+
var mkrs = opts["markers"];
|
|
5351
|
+
isArray(mkrs) && this.addMarkers(mkrs);
|
|
5352
|
+
};
|
|
5353
|
+
|
|
5354
|
+
/**
|
|
5355
|
+
* 添加要聚合的标记数组。
|
|
5356
|
+
* @param {Array<Marker>} markers 要聚合的标记数组
|
|
5357
|
+
*
|
|
5358
|
+
* @return 无返回值。
|
|
5359
|
+
*/
|
|
5360
|
+
MarkerClusterer$1.prototype.addMarkers = function(markers){
|
|
5361
|
+
if (!markers.length) {
|
|
5362
|
+
return
|
|
5363
|
+
}
|
|
5364
|
+
for(var i = 0, len = markers.length; i <len ; i++){
|
|
5365
|
+
this._pushMarkerTo(markers[i]);
|
|
5366
|
+
}
|
|
5367
|
+
this._createClusters();
|
|
5368
|
+
};
|
|
5369
|
+
|
|
5370
|
+
/**
|
|
5371
|
+
* 把一个标记添加到要聚合的标记数组中
|
|
5372
|
+
* @param {BMap.Marker} marker 要添加的标记
|
|
5373
|
+
*
|
|
5374
|
+
* @return 无返回值。
|
|
5375
|
+
*/
|
|
5376
|
+
MarkerClusterer$1.prototype._pushMarkerTo = function(marker){
|
|
5377
|
+
var index = indexOf(marker, this._markers);
|
|
5378
|
+
if(index === -1){
|
|
5379
|
+
marker.isInCluster = false;
|
|
5380
|
+
this._markers.push(marker);//Marker拖放后enableDragging不做变化,忽略
|
|
5381
|
+
}
|
|
5382
|
+
};
|
|
5383
|
+
|
|
5384
|
+
/**
|
|
5385
|
+
* 添加一个聚合的标记。
|
|
5386
|
+
* @param {BMap.Marker} marker 要聚合的单个标记。
|
|
5387
|
+
* @return 无返回值。
|
|
5388
|
+
*/
|
|
5389
|
+
MarkerClusterer$1.prototype.addMarker = function(marker) {
|
|
5390
|
+
this._pushMarkerTo(marker);
|
|
5391
|
+
this._createClusters();
|
|
5392
|
+
};
|
|
5393
|
+
|
|
5394
|
+
/**
|
|
5395
|
+
* 根据所给定的标记,创建聚合点
|
|
5396
|
+
* @return 无返回值
|
|
5397
|
+
*/
|
|
5398
|
+
MarkerClusterer$1.prototype._createClusters = function(){
|
|
5399
|
+
var mapBounds = this._map.getBounds();
|
|
5400
|
+
if (!mapBounds.getCenter()) {
|
|
5401
|
+
return
|
|
5402
|
+
}
|
|
5403
|
+
var extendedBounds = getExtendedBounds(this._map, mapBounds, this._gridSize);
|
|
5404
|
+
for(var i = 0, marker; marker = this._markers[i]; i++){
|
|
5405
|
+
if(!marker.isInCluster && extendedBounds.containsPoint(marker.getPosition()) ){
|
|
5406
|
+
this._addToClosestCluster(marker);
|
|
5407
|
+
}
|
|
5408
|
+
}
|
|
5409
|
+
var len = this._markers.length;
|
|
5410
|
+
for (var i = 0; i < len; i++) {
|
|
5411
|
+
if(this._clusters[i]){
|
|
5412
|
+
this._clusters[i].render();
|
|
5413
|
+
}
|
|
5414
|
+
}
|
|
5415
|
+
};
|
|
5416
|
+
|
|
5417
|
+
/**
|
|
5418
|
+
* 根据标记的位置,把它添加到最近的聚合中
|
|
5419
|
+
* @param {BMap.Marker} marker 要进行聚合的单个标记
|
|
5420
|
+
*
|
|
5421
|
+
* @return 无返回值。
|
|
5422
|
+
*/
|
|
5423
|
+
MarkerClusterer$1.prototype._addToClosestCluster = function (marker){
|
|
5424
|
+
var distance = 4000000;
|
|
5425
|
+
var clusterToAddTo = null;
|
|
5426
|
+
marker.getPosition();
|
|
5427
|
+
for(var i = 0, cluster; cluster = this._clusters[i]; i++){
|
|
5428
|
+
var center = cluster.getCenter();
|
|
5429
|
+
if(center){
|
|
5430
|
+
var d = this._map.getDistance(center, marker.getPosition());
|
|
5431
|
+
if(d < distance){
|
|
5432
|
+
distance = d;
|
|
5433
|
+
clusterToAddTo = cluster;
|
|
5434
|
+
}
|
|
5435
|
+
}
|
|
5436
|
+
}
|
|
5437
|
+
|
|
5438
|
+
if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)){
|
|
5439
|
+
clusterToAddTo.addMarker(marker);
|
|
5440
|
+
} else {
|
|
5441
|
+
var cluster = new Cluster(this);
|
|
5442
|
+
cluster.addMarker(marker);
|
|
5443
|
+
this._clusters.push(cluster);
|
|
5444
|
+
}
|
|
5445
|
+
};
|
|
5446
|
+
|
|
5447
|
+
/**
|
|
5448
|
+
* 清除上一次的聚合的结果
|
|
5449
|
+
* @return 无返回值。
|
|
5450
|
+
*/
|
|
5451
|
+
MarkerClusterer$1.prototype._clearLastClusters = function(){
|
|
5452
|
+
for(var i = 0, cluster; cluster = this._clusters[i]; i++){
|
|
5453
|
+
cluster.remove();
|
|
5454
|
+
}
|
|
5455
|
+
this._clusters = [];//置空Cluster数组
|
|
5456
|
+
this._removeMarkersFromCluster();//把Marker的cluster标记设为false
|
|
5457
|
+
};
|
|
5458
|
+
|
|
5459
|
+
/**
|
|
5460
|
+
* 清除某个聚合中的所有标记
|
|
5461
|
+
* @return 无返回值
|
|
5462
|
+
*/
|
|
5463
|
+
MarkerClusterer$1.prototype._removeMarkersFromCluster = function(){
|
|
5464
|
+
for(var i = 0, marker; marker = this._markers[i]; i++){
|
|
5465
|
+
marker.isInCluster = false;
|
|
5466
|
+
}
|
|
5467
|
+
};
|
|
5468
|
+
|
|
5469
|
+
/**
|
|
5470
|
+
* 把所有的标记从地图上清除
|
|
5471
|
+
* @return 无返回值
|
|
5472
|
+
*/
|
|
5473
|
+
MarkerClusterer$1.prototype._removeMarkersFromMap = function(){
|
|
5474
|
+
for(var i = 0, marker; marker = this._markers[i]; i++){
|
|
5475
|
+
marker.isInCluster = false;
|
|
5476
|
+
var label = marker.getLabel();
|
|
5477
|
+
this._map.removeOverlay(marker);
|
|
5478
|
+
marker.setLabel(label);
|
|
5479
|
+
}
|
|
5480
|
+
};
|
|
5481
|
+
|
|
5482
|
+
/**
|
|
5483
|
+
* 删除单个标记
|
|
5484
|
+
* @param {BMap.Marker} marker 需要被删除的marker
|
|
5485
|
+
*
|
|
5486
|
+
* @return {Boolean} 删除成功返回true,否则返回false
|
|
5487
|
+
*/
|
|
5488
|
+
MarkerClusterer$1.prototype._removeMarker = function(marker) {
|
|
5489
|
+
var index = indexOf(marker, this._markers);
|
|
5490
|
+
if (index === -1) {
|
|
5491
|
+
return false;
|
|
5492
|
+
}
|
|
5493
|
+
this._map.removeOverlay(marker);
|
|
5494
|
+
this._markers.splice(index, 1);
|
|
5495
|
+
return true;
|
|
5496
|
+
};
|
|
5497
|
+
|
|
5498
|
+
/**
|
|
5499
|
+
* 删除单个标记
|
|
5500
|
+
* @param {BMap.Marker} marker 需要被删除的marker
|
|
5501
|
+
*
|
|
5502
|
+
* @return {Boolean} 删除成功返回true,否则返回false
|
|
5503
|
+
*/
|
|
5504
|
+
MarkerClusterer$1.prototype.removeMarker = function(marker) {
|
|
5505
|
+
var success = this._removeMarker(marker);
|
|
5506
|
+
if (success) {
|
|
5507
|
+
this._clearLastClusters();
|
|
5508
|
+
this._createClusters();
|
|
5509
|
+
}
|
|
5510
|
+
return success;
|
|
5511
|
+
};
|
|
5512
|
+
|
|
5513
|
+
/**
|
|
5514
|
+
* 删除一组标记
|
|
5515
|
+
* @param {Array<BMap.Marker>} markers 需要被删除的marker数组
|
|
5516
|
+
*
|
|
5517
|
+
* @return {Boolean} 删除成功返回true,否则返回false
|
|
5518
|
+
*/
|
|
5519
|
+
MarkerClusterer$1.prototype.removeMarkers = function(markers) {
|
|
5520
|
+
var success = false;
|
|
5521
|
+
for (var i = 0; i < markers.length; i++) {
|
|
5522
|
+
var r = this._removeMarker(markers[i]);
|
|
5523
|
+
success = success || r;
|
|
5524
|
+
}
|
|
5525
|
+
|
|
5526
|
+
if (success) {
|
|
5527
|
+
this._clearLastClusters();
|
|
5528
|
+
this._createClusters();
|
|
5529
|
+
}
|
|
5530
|
+
return success;
|
|
5531
|
+
};
|
|
5532
|
+
|
|
5533
|
+
/**
|
|
5534
|
+
* 从地图上彻底清除所有的标记
|
|
5535
|
+
* @return 无返回值
|
|
5536
|
+
*/
|
|
5537
|
+
MarkerClusterer$1.prototype.clearMarkers = function() {
|
|
5538
|
+
this._clearLastClusters();
|
|
5539
|
+
this._removeMarkersFromMap();
|
|
5540
|
+
this._markers = [];
|
|
5541
|
+
};
|
|
5542
|
+
|
|
5543
|
+
/**
|
|
5544
|
+
* 重新生成,比如改变了属性等
|
|
5545
|
+
* @return 无返回值
|
|
5546
|
+
*/
|
|
5547
|
+
MarkerClusterer$1.prototype._redraw = function () {
|
|
5548
|
+
this._clearLastClusters();
|
|
5549
|
+
this._createClusters();
|
|
5550
|
+
};
|
|
5551
|
+
|
|
5552
|
+
/**
|
|
5553
|
+
* 获取网格大小
|
|
5554
|
+
* @return {Number} 网格大小
|
|
5555
|
+
*/
|
|
5556
|
+
MarkerClusterer$1.prototype.getGridSize = function() {
|
|
5557
|
+
return this._gridSize;
|
|
5558
|
+
};
|
|
5559
|
+
|
|
5560
|
+
/**
|
|
5561
|
+
* 设置网格大小
|
|
5562
|
+
* @param {Number} size 网格大小
|
|
5563
|
+
* @return 无返回值
|
|
5564
|
+
*/
|
|
5565
|
+
MarkerClusterer$1.prototype.setGridSize = function(size) {
|
|
5566
|
+
this._gridSize = size;
|
|
5567
|
+
this._redraw();
|
|
5568
|
+
};
|
|
5569
|
+
|
|
5570
|
+
/**
|
|
5571
|
+
* 获取聚合的最大缩放级别。
|
|
5572
|
+
* @return {Number} 聚合的最大缩放级别。
|
|
5573
|
+
*/
|
|
5574
|
+
MarkerClusterer$1.prototype.getMaxZoom = function() {
|
|
5575
|
+
return this._maxZoom;
|
|
5576
|
+
};
|
|
5577
|
+
|
|
5578
|
+
/**
|
|
5579
|
+
* 设置聚合的最大缩放级别
|
|
5580
|
+
* @param {Number} maxZoom 聚合的最大缩放级别
|
|
5581
|
+
* @return 无返回值
|
|
5582
|
+
*/
|
|
5583
|
+
MarkerClusterer$1.prototype.setMaxZoom = function(maxZoom) {
|
|
5584
|
+
this._maxZoom = maxZoom;
|
|
5585
|
+
this._redraw();
|
|
5586
|
+
};
|
|
5587
|
+
|
|
5588
|
+
/**
|
|
5589
|
+
* 获取聚合图标的z-index
|
|
5590
|
+
* @return {number | undefined}
|
|
5591
|
+
*/
|
|
5592
|
+
MarkerClusterer$1.prototype.getZIndex = function () {
|
|
5593
|
+
return this._zIndex;
|
|
5594
|
+
};
|
|
5595
|
+
|
|
5596
|
+
/**
|
|
5597
|
+
* 设置聚合图标的z-index
|
|
5598
|
+
* @param {number} zIndex
|
|
5599
|
+
*/
|
|
5600
|
+
MarkerClusterer$1.prototype.setZIndex = function (zIndex) {
|
|
5601
|
+
if (this._zIndex != zIndex) {
|
|
5602
|
+
this.zIndex = zIndex;
|
|
5603
|
+
this._redraw();
|
|
5604
|
+
}
|
|
5605
|
+
};
|
|
5606
|
+
|
|
5607
|
+
/**
|
|
5608
|
+
* 获取聚合的样式风格集合
|
|
5609
|
+
* @return {Array<IconStyle>} 聚合的样式风格集合
|
|
5610
|
+
*/
|
|
5611
|
+
MarkerClusterer$1.prototype.getStyles = function() {
|
|
5612
|
+
return this._styles;
|
|
5613
|
+
};
|
|
5614
|
+
|
|
5615
|
+
/**
|
|
5616
|
+
* 设置聚合的样式风格集合
|
|
5617
|
+
* @param {Array<IconStyle>} styles 样式风格数组
|
|
5618
|
+
* @return 无返回值
|
|
5619
|
+
*/
|
|
5620
|
+
MarkerClusterer$1.prototype.setStyles = function(styles) {
|
|
5621
|
+
this._styles = styles;
|
|
5622
|
+
this._redraw();
|
|
5623
|
+
};
|
|
5624
|
+
|
|
5625
|
+
/**
|
|
5626
|
+
* 获取单个聚合的最小数量。
|
|
5627
|
+
* @return {Number} 单个聚合的最小数量。
|
|
5628
|
+
*/
|
|
5629
|
+
MarkerClusterer$1.prototype.getMinClusterSize = function() {
|
|
5630
|
+
return this._minClusterSize;
|
|
5631
|
+
};
|
|
5632
|
+
|
|
5633
|
+
/**
|
|
5634
|
+
* 设置单个聚合的最小数量。
|
|
5635
|
+
* @param {Number} size 单个聚合的最小数量。
|
|
5636
|
+
* @return 无返回值。
|
|
5637
|
+
*/
|
|
5638
|
+
MarkerClusterer$1.prototype.setMinClusterSize = function(size) {
|
|
5639
|
+
this._minClusterSize = size;
|
|
5640
|
+
this._redraw();
|
|
5641
|
+
};
|
|
5642
|
+
|
|
5643
|
+
/**
|
|
5644
|
+
* 获取单个聚合的落脚点是否是聚合内所有标记的平均中心。
|
|
5645
|
+
* @return {Boolean} true或false。
|
|
5646
|
+
*/
|
|
5647
|
+
MarkerClusterer$1.prototype.isAverageCenter = function() {
|
|
5648
|
+
return this._isAverageCenter;
|
|
5649
|
+
};
|
|
5650
|
+
|
|
5651
|
+
/**
|
|
5652
|
+
* 获取聚合的Map实例。
|
|
5653
|
+
* @return {BMap.Map} Map的示例。
|
|
5654
|
+
*/
|
|
5655
|
+
MarkerClusterer$1.prototype.getMap = function() {
|
|
5656
|
+
return this._map;
|
|
5657
|
+
};
|
|
5658
|
+
|
|
5659
|
+
/**
|
|
5660
|
+
* 获取所有的标记数组。
|
|
5661
|
+
* @return {Array<Marker>} 标记数组。
|
|
5662
|
+
*/
|
|
5663
|
+
MarkerClusterer$1.prototype.getMarkers = function() {
|
|
5664
|
+
return this._markers;
|
|
5665
|
+
};
|
|
5666
|
+
|
|
5667
|
+
/**
|
|
5668
|
+
* 获取聚合的总数量。
|
|
5669
|
+
* @return {Number} 聚合的总数量。
|
|
5670
|
+
*/
|
|
5671
|
+
MarkerClusterer$1.prototype.getClustersCount = function() {
|
|
5672
|
+
var count = 0;
|
|
5673
|
+
for(var i = 0, cluster; cluster = this._clusters[i]; i++){
|
|
5674
|
+
cluster.isReal() && count++;
|
|
5675
|
+
}
|
|
5676
|
+
return count;
|
|
5677
|
+
};
|
|
5678
|
+
|
|
5679
|
+
/**
|
|
5680
|
+
* 用于计算cluster的文本
|
|
5681
|
+
* @param {Array<BMap.Marker>} markers cluster的markers
|
|
5682
|
+
* @return {String}
|
|
5683
|
+
*/
|
|
5684
|
+
MarkerClusterer$1.prototype._textCalculator = function(markers){
|
|
5685
|
+
return markers.length.toString();
|
|
5686
|
+
};
|
|
5687
|
+
|
|
5688
|
+
/**
|
|
5689
|
+
* 用于计算cluster的styles的索引
|
|
5690
|
+
* @param {Array<BMap.Marker>} markers cluster的markers
|
|
5691
|
+
* @param {Number} stylesLength styles的总长度
|
|
5692
|
+
* @return {Number} styles的索引
|
|
5693
|
+
*/
|
|
5694
|
+
MarkerClusterer$1.prototype._stylesIndexCalculator = null;/* function(markers, stylesLength){
|
|
5695
|
+
return -1;
|
|
5696
|
+
}*/
|
|
5697
|
+
|
|
5698
|
+
/**
|
|
5699
|
+
* 设置计算cluster的文本的计算函数
|
|
5700
|
+
* @param {function(Array<BMap.Marker>)} calculator
|
|
5701
|
+
*/
|
|
5702
|
+
MarkerClusterer$1.prototype.setTextCalculator = function(calculator){
|
|
5703
|
+
this._textCalculator = calculator;
|
|
5704
|
+
};
|
|
5705
|
+
|
|
5706
|
+
/**
|
|
5707
|
+
* 设置计算cluster的styles的索引的计算函数
|
|
5708
|
+
* @param {function(Array<BMap.Marker>, Number)} calculator
|
|
5709
|
+
*/
|
|
5710
|
+
MarkerClusterer$1.prototype.setStylesIndexCalculator = function(calculator){
|
|
5711
|
+
this._stylesIndexCalculator = calculator;
|
|
5712
|
+
};
|
|
5713
|
+
|
|
5714
|
+
/**
|
|
5715
|
+
* @ignore
|
|
5716
|
+
* Cluster
|
|
5717
|
+
* @class 表示一个聚合对象,该聚合,包含有N个标记,这N个标记组成的范围,并有予以显示在Map上的TextIconOverlay等。
|
|
5718
|
+
* @constructor
|
|
5719
|
+
* @param {MarkerClusterer} markerClusterer 一个标记聚合器示例。
|
|
5720
|
+
*/
|
|
5721
|
+
function Cluster(markerClusterer){
|
|
5722
|
+
var that = this;
|
|
5723
|
+
this._markerClusterer = markerClusterer;
|
|
5724
|
+
this._map = markerClusterer.getMap();
|
|
5725
|
+
this._minClusterSize = markerClusterer.getMinClusterSize();
|
|
5726
|
+
this._isAverageCenter = markerClusterer.isAverageCenter();
|
|
5727
|
+
this._center = null;//落脚位置
|
|
5728
|
+
this._markers = [];//这个Cluster中所包含的markers
|
|
5729
|
+
this._gridBounds = null;//以中心点为准,向四边扩大gridSize个像素的范围,也即网格范围
|
|
5730
|
+
this._isReal = false; //真的是个聚合
|
|
5731
|
+
|
|
5732
|
+
this._clusterMarker = new TextIconOverlay(this._center, this._markers.length, {
|
|
5733
|
+
"styles": this._markerClusterer.getStyles(),
|
|
5734
|
+
zIndex: markerClusterer._zIndex
|
|
5735
|
+
});
|
|
5736
|
+
this._clusterMarker.getStyleByText = function (text, styles) {
|
|
5737
|
+
if (that._markerClusterer._stylesIndexCalculator) {
|
|
5738
|
+
var index = that._markerClusterer._stylesIndexCalculator(that._markers, styles.length);
|
|
5739
|
+
return styles[index];
|
|
5740
|
+
}
|
|
5741
|
+
return TextIconOverlay.prototype.getStyleByText.call(this, text, styles);
|
|
5742
|
+
};
|
|
5743
|
+
//this._map.addOverlay(this._clusterMarker);
|
|
5744
|
+
this._clusterMarker.addEventListener && this._clusterMarker.addEventListener("click", function (event) {
|
|
5745
|
+
that._map.setViewport(that.getBounds());
|
|
5746
|
+
});
|
|
5747
|
+
}
|
|
5748
|
+
|
|
5749
|
+
/**
|
|
5750
|
+
* 向该聚合添加一个标记。
|
|
5751
|
+
* @param {Marker} marker 要添加的标记。
|
|
5752
|
+
* @return 无返回值。
|
|
5753
|
+
*/
|
|
5754
|
+
Cluster.prototype.addMarker = function(marker){
|
|
5755
|
+
if(this.isMarkerInCluster(marker)){
|
|
5756
|
+
return false;
|
|
5757
|
+
}//也可用marker.isInCluster判断,外面判断OK,这里基本不会命中
|
|
5758
|
+
|
|
5759
|
+
if (!this._center){
|
|
5760
|
+
this._center = marker.getPosition();
|
|
5761
|
+
this.updateGridBounds();//
|
|
5762
|
+
} else {
|
|
5763
|
+
if(this._isAverageCenter){
|
|
5764
|
+
var l = this._markers.length + 1;
|
|
5765
|
+
var lat = (this._center.lat * (l - 1) + marker.getPosition().lat) / l;
|
|
5766
|
+
var lng = (this._center.lng * (l - 1) + marker.getPosition().lng) / l;
|
|
5767
|
+
this._center = new BMap.Point(lng, lat);
|
|
5768
|
+
this.updateGridBounds();
|
|
5769
|
+
}//计算新的Center
|
|
5770
|
+
}
|
|
5771
|
+
|
|
5772
|
+
marker.isInCluster = true;
|
|
5773
|
+
this._markers.push(marker);
|
|
5774
|
+
|
|
5775
|
+
// var len = this._markers.length;
|
|
5776
|
+
// if(len < this._minClusterSize ){
|
|
5777
|
+
// this._map.addOverlay(marker);
|
|
5778
|
+
// //this.updateClusterMarker();
|
|
5779
|
+
// return true;
|
|
5780
|
+
// } else if (len === this._minClusterSize) {
|
|
5781
|
+
// for (var i = 0; i < len; i++) {
|
|
5782
|
+
// var label = this._markers[i].getLabel();
|
|
5783
|
+
// this._markers[i].getMap() && this._map.removeOverlay(this._markers[i]);
|
|
5784
|
+
// this._markers[i].setLabel(label);
|
|
5785
|
+
// }
|
|
5786
|
+
|
|
5787
|
+
// }
|
|
5788
|
+
// this._map.addOverlay(this._clusterMarker);
|
|
5789
|
+
// this._isReal = true;
|
|
5790
|
+
// this.updateClusterMarker();
|
|
5791
|
+
return true;
|
|
5792
|
+
};
|
|
5793
|
+
|
|
5794
|
+
Cluster.prototype.render = function(){
|
|
5795
|
+
var len = this._markers.length;
|
|
5796
|
+
|
|
5797
|
+
if (len < this._minClusterSize) {
|
|
5798
|
+
for (var i = 0; i < len; i++) {
|
|
5799
|
+
this._map.addOverlay(this._markers[i]);
|
|
5800
|
+
}
|
|
5801
|
+
} else {
|
|
5802
|
+
this._map.addOverlay(this._clusterMarker);
|
|
5803
|
+
this._isReal = true;
|
|
5804
|
+
this.updateClusterMarker();
|
|
5805
|
+
}
|
|
5806
|
+
};
|
|
5807
|
+
|
|
5808
|
+
/**
|
|
5809
|
+
* 判断一个标记是否在该聚合中。
|
|
5810
|
+
* @param {Marker} marker 要判断的标记。
|
|
5811
|
+
* @return {Boolean} true或false。
|
|
5812
|
+
*/
|
|
5813
|
+
Cluster.prototype.isMarkerInCluster= function(marker){
|
|
5814
|
+
if (this._markers.indexOf) {
|
|
5815
|
+
return this._markers.indexOf(marker) != -1;
|
|
5816
|
+
} else {
|
|
5817
|
+
for (var i = 0, m; m = this._markers[i]; i++) {
|
|
5818
|
+
if (m === marker) {
|
|
5819
|
+
return true;
|
|
5820
|
+
}
|
|
5821
|
+
}
|
|
5822
|
+
}
|
|
5823
|
+
return false;
|
|
5824
|
+
};
|
|
5825
|
+
|
|
5826
|
+
/**
|
|
5827
|
+
* 判断一个标记是否在该聚合网格范围中。
|
|
5828
|
+
* @param {Marker} marker 要判断的标记。
|
|
5829
|
+
* @return {Boolean} true或false。
|
|
5830
|
+
*/
|
|
5831
|
+
Cluster.prototype.isMarkerInClusterBounds = function(marker) {
|
|
5832
|
+
return this._gridBounds.containsPoint(marker.getPosition());
|
|
5833
|
+
};
|
|
5834
|
+
|
|
5835
|
+
Cluster.prototype.isReal = function(marker) {
|
|
5836
|
+
return this._isReal;
|
|
5837
|
+
};
|
|
5838
|
+
|
|
5839
|
+
/**
|
|
5840
|
+
* 更新该聚合的网格范围。
|
|
5841
|
+
* @return 无返回值。
|
|
5842
|
+
*/
|
|
5843
|
+
Cluster.prototype.updateGridBounds = function() {
|
|
5844
|
+
var bounds = new BMap.Bounds(this._center, this._center);
|
|
5845
|
+
this._gridBounds = getExtendedBounds(this._map, bounds, this._markerClusterer.getGridSize());
|
|
5846
|
+
};
|
|
5847
|
+
|
|
5848
|
+
/**
|
|
5849
|
+
* 更新该聚合的显示样式,也即TextIconOverlay。
|
|
5850
|
+
* @return 无返回值。
|
|
5851
|
+
*/
|
|
5852
|
+
Cluster.prototype.updateClusterMarker = function () {
|
|
5853
|
+
if (this._map.getZoom() > this._markerClusterer.getMaxZoom()) {
|
|
5854
|
+
this._clusterMarker && this._map.removeOverlay(this._clusterMarker);
|
|
5855
|
+
for (var i = 0, marker; marker = this._markers[i]; i++) {
|
|
5856
|
+
this._map.addOverlay(marker);
|
|
5857
|
+
}
|
|
5858
|
+
return;
|
|
5859
|
+
}
|
|
5860
|
+
|
|
5861
|
+
if (this._markers.length < this._minClusterSize) {
|
|
5862
|
+
this._clusterMarker.hide();
|
|
5863
|
+
return;
|
|
5864
|
+
}
|
|
5865
|
+
|
|
5866
|
+
this._clusterMarker.setPosition(this._center);
|
|
5867
|
+
|
|
5868
|
+
this._clusterMarker.setText(this._markerClusterer._textCalculator(this._markers));
|
|
5869
|
+
};
|
|
5870
|
+
|
|
5871
|
+
/**
|
|
5872
|
+
* 删除该聚合。
|
|
5873
|
+
* @return 无返回值。
|
|
5874
|
+
*/
|
|
5875
|
+
Cluster.prototype.remove = function(){
|
|
5876
|
+
for (var i = 0, m; m = this._markers[i]; i++) {
|
|
5877
|
+
var label = this._markers[i].getLabel();
|
|
5878
|
+
this._markers[i].getMap() && this._map.removeOverlay(this._markers[i]);
|
|
5879
|
+
this._markers[i].setLabel(label);
|
|
5880
|
+
}//清除散的标记点
|
|
5881
|
+
this._map.removeOverlay(this._clusterMarker);
|
|
5882
|
+
this._markers.length = 0;
|
|
5883
|
+
delete this._markers;
|
|
5884
|
+
};
|
|
5885
|
+
|
|
5886
|
+
/**
|
|
5887
|
+
* 获取该聚合所包含的所有标记的最小外接矩形的范围。
|
|
5888
|
+
* @return {BMap.Bounds} 计算出的范围。
|
|
5889
|
+
*/
|
|
5890
|
+
Cluster.prototype.getBounds = function() {
|
|
5891
|
+
var bounds = new BMap.Bounds(this._center,this._center);
|
|
5892
|
+
for (var i = 0, marker; marker = this._markers[i]; i++) {
|
|
5893
|
+
bounds.extend(marker.getPosition());
|
|
5894
|
+
}
|
|
5895
|
+
return bounds;
|
|
5896
|
+
};
|
|
5897
|
+
|
|
5898
|
+
/**
|
|
5899
|
+
* 获取该聚合的落脚点。
|
|
5900
|
+
* @return {BMap.Point} 该聚合的落脚点。
|
|
5901
|
+
*/
|
|
5902
|
+
Cluster.prototype.getCenter = function() {
|
|
5903
|
+
return this._center;
|
|
5904
|
+
};
|
|
5905
|
+
|
|
1874
5906
|
// cspell: ignore bmaplib, markerclusterer, texticonoverlay
|
|
1875
5907
|
var ClusterIconStyle;
|
|
1876
5908
|
(function (ClusterIconStyle) {
|
|
@@ -1958,7 +5990,7 @@ class BaiduMarkerClusterer extends MarkerClusterer {
|
|
|
1958
5990
|
// ## vite:
|
|
1959
5991
|
// import导入是异步的, 故无法写在这里, 改成在模块顶部直接导入
|
|
1960
5992
|
// TODO: 2023/03/16 ipcjs 会不会有初始化时机的问题?
|
|
1961
|
-
this.inner = new
|
|
5993
|
+
this.inner = new MarkerClusterer$1(map.innerMap, {
|
|
1962
5994
|
// baidu的构造器中的markers, 会触发重绘, 会导致使用旧的calculator计算cluster
|
|
1963
5995
|
// markers: options.markers?.map(it => (it as BaiduMarker).innerOverlay),
|
|
1964
5996
|
gridSize: options.gridSize,
|
|
@@ -1987,7 +6019,7 @@ class GoogleMarkerClusterer extends MarkerClusterer {
|
|
|
1987
6019
|
inner;
|
|
1988
6020
|
constructor(map, options) {
|
|
1989
6021
|
super(map);
|
|
1990
|
-
this.inner = new
|
|
6022
|
+
this.inner = new MarkerClusterer$2(map.innerMap,
|
|
1991
6023
|
// google的构造器中的markers,不会触发重绘, 第一次绘制通过监听地图的'idle'事件触发, 可以直接设置
|
|
1992
6024
|
options.markers?.map(it => it.innerOverlay), {
|
|
1993
6025
|
gridSize: options.gridSize,
|