山海鲸引入天地图目前只有 iframe 的方式引入
首先我们创建一个文件夹
——index.html
——index.js
——data.js
大家都是大佬,我就不详细介绍了,上代码都能看得懂
首先是index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title>天地图</title>
<script type="text/javascript" src="http://api.tianditu.gov.cn/api?v=4.0&tk=天地图申请的key">
</script>
<style>
body,
html {
width: 100%;
height: 100%;
margin: 0;
font-family: "Microsoft YaHei",serif
}
#mapDiv {
width: 100%;
height: 100%;
}
input,
b,
p {
margin-left: 5px;
font-size: 14px
}
</style>
</head>
<script src="./index.js" defer></script>
<script src="./data.js" defer></script>
<body onload="onLoad()">
<div id="mapDiv">
</div>
</body>
</html>
index.html里面就是创建了基础的地图元素和引入方法 没啥东西
接下来是index.js
let map;
const zoom = 13; //缩放大小
let polygonInitArray = null; //保存默认地块数组
function onLoad() {
//初始化地图对象
map = new T.Map("mapDiv");
//设置显示地图的中心点和级别
map.centerAndZoom(new T.LngLat(102.85428, 24.82373), zoom);
map.setMinZoom(12); // 设置最大缩放级别
map.setStyle("indigo"); // 设置地图风格
//允许鼠标滚轮缩放地图
map.enableScrollWheelZoom();
// 第一块
const points1 = generatePolygonArray(lineData1);
const polygon1 = createLandAndSetStyle(points1, "#fff", 2, 0.5, "#0BFA01", 1);
// .....很多块
polygonInitArray = [
polygon1,
//....很多块
];
polygonInitArray.forEach((polygon) => map.addOverLay(polygon));
// 生成地块数组函数
function generatePolygonArray(data) {
return data.map(({ lng, lat }) => new T.LngLat(lng, lat));
}
// 创建地块并为地块设置颜色的函数
function createLandAndSetStyle(
targetObj,
color,
weight,
opacity,
fillColor,
fillOpacity,
lineStyle = "solid"
) {
return new T.Polygon(targetObj, {
color,
weight,
opacity,
fillColor,
fillOpacity,
lineStyle,
});
}
}
上面就是创建块的函数,还缺的就是组成这个块的每一个点的经纬度
接下来是data.js
const lineData1 = [
{ lng: 102.83233134, lat: 24.8511322 },
{ lng: 102.84071464, lat: 24.85366787 },
{ lng: 102.8415131, lat: 24.85049824 },
{ lng: 102.83971668, lat: 24.8482342 },
{ lng: 102.83742126, lat: 24.84705688 },
{ lng: 102.83402801, lat: 24.84841532 },
{ lng: 102.83233134, lat: 24.8511322 },
];
看不懂的直接复制代码,运行了就知道了,注意 天地图的key要自己申请
上面讲的是地图上勾画面
接下来我们说天地图热力图怎么画
首先也是创建文件夹
包含
——index.html
——index.js
——heatMap.js(这个是生成热力图关键的js不需要改动)
这里我没有把数据放data.js里面,全放index.js里面,懒了
首先index.html里面没啥改动,跟上面差不多
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>天地图-地图API-范例-地图加载单图层</title>
<script
type="text/javascript"
src="http://api.tianditu.gov.cn/api?v=4.0&tk=申请的天地图key"
></script>
<style>
body,
html {
width: 100%;
height: 100%;
margin: 0;
font-family: "Microsoft YaHei", serif;
}
#mapDiv {
width: 100%;
height: 100%;
}
input,
b,
p {
margin-left: 5px;
font-size: 14px;
}
</style>
</head>
<script src="./index.js" defer></script>
<script src="./heatMap.js" defer></script>
<body onload="onLoad()">
<div id="mapDiv"></div>
</body>
</html>
接下来index.js
let map;
const zoom = 13;
function onLoad() {
//初始化地图对象
map = new T.Map("mapDiv");
//设置显示地图的中心点和级别
map.centerAndZoom(new T.LngLat(102.85428, 24.82373), zoom);
map.setMinZoom(12); // 设置最大缩放级别
map.setStyle("indigo"); // 设置地图风格
//允许鼠标滚轮缩放地图
map.enableScrollWheelZoom();
// 创建一块热力图
//参数说明如下:
/* visible 热力图是否显示,默认为true
* opacity 热力的透明度,1-100
* radius 势力图的每个点的半径大小
* gradient {JSON} 热力图的渐变区间 . gradient如下所示
* {
.2:'rgb(0, 255, 255)',
.5:'rgb(0, 110, 255)',
.8:'rgb(100, 0, 255)'
}
value 为颜色值.
*/
const data = [
{ name: "test1", value: 190 },
{ name: "test2", value: 180 },
{ name: "test3", value: 170 },
{ name: "test4", value: 105 },
{ name: "test5", value: 95 },
{ name: "test6", value: 134 },
{ name: "test7", value: 167 },
{ name: "test8", value: 178 },
{ name: "test9", value: 87 },
{ name: "test10", value: 178 },
{ name: "test11", value: 147 },
{ name: "test12", value: 87 },
{ name: "test13", value: 100 },
{ name: "test14", value: 124 },
];
const geoCoordMap = {
test1: [102.85428, 24.82373],
test2: [102.88428, 24.85373],
test3: [102.89428, 24.83373],
test4: [102.84303354, 24.80530684],
test5: [102.84071464, 24.85366787],
test6: [102.82823951, 24.85040768],
test7: [102.83991634, 24.84270977],
test8: [102.84370878, 24.84479278],
test9: [102.84150136, 24.82757133],
test10: [102.83916618, 24.8021391],
test11: [102.88084795, 24.84942571],
test12: [102.87013422, 24.81139329],
test13: [102.88492484, 24.84942571],
test14: [102.8701342, 24.81139329],
};
var convertData = function (data) {
var res = [];
for (var i = 0; i < data.length; i++) {
var geoCoord = geoCoordMap[data[i].name];
if (geoCoord) {
res.push({
name: data[i].name,
lat: geoCoord[1],
lng: geoCoord[0],
count: data[i].value,
});
}
}
return res;
};
var points = convertData(data);
let heatmapOverlay = new T.HeatmapOverlay({
radius: 30,
visible: true,
gradient: {
0.2: "#FAFFA8FF",
0.5: "#FF8C12FF",
0.8: "#F51D27FF",
},
});
map.addOverLay(heatmapOverlay);
heatmapOverlay.setDataSet({ data: points, max: 300 });
}
接下来就是一个heatMap.js文件,这个是我自己再天地图示例里面复制源码来的,下载要上github,懒得去,你们要有直接复制用
/**
* 浠ョ壒娈婇珮浜殑褰㈠紡鐩磋灞曠ず鏁版嵁鍒嗗竷鐘跺喌銆�
* 娉細chrome銆乻afari銆両E9鍙婁互涓婃祻瑙堝櫒锛屾牳蹇冪殑浠g爜涓昏鏉ヨ嚜浜庣涓夋柟heatmap.js銆侤author juyang
*/
T.HeatmapOverlay = T.Overlay.extend({
/**
*鏋勯€�
* @param options
*/
initialize: function (options) {
this.conf = options;
this.heatmap = null;
this.latlngs = [];
this.bounds = null;
},
onAdd: function (map) {
this._map = map;
var el = document.createElement("div");
el.style.position = "absolute";
el.style.top = 0;
el.style.left = 0;
el.style.border = 0;
el.style.width = this._map.getSize().x + "px";
el.style.height = this._map.getSize().y + "px";
this.conf.container = el;
if (!this.isSupportCanvas()) {
//鍒ゆ柇鏄惁鏀寔Canvas.
return el;
}
this.conf.valueField = this.conf.valueField || "count";
this.conf.latField = this.conf.latField || "lat";
this.conf.lngField = this.conf.lngField || "lng";
this.heatmap = h337.create(this.conf);
this.heatmap._renderer.setDimensions(
this._map.getSize().x,
this._map.getSize().y
);
map.getPanes().overlayPane.appendChild(el);
map.on("moveend", this._reset, this);
this._div = el;
},
onRemove: function (map) {
map.getPanes().overlayPane.removeChild(this._div);
map.off("moveend", this._reset, this);
},
_reset: function () {
var size = this._map.getSize();
this._div.style.width = size.x + "px";
this._div.style.height = size.y + "px";
this.heatmap._renderer.setDimensions(size.x, size.y);
this.draw();
},
getElement: function () {
return this.conf.container;
},
draw: function () {
if (!this.isSupportCanvas()) {
//鍒ゆ柇鏄惁鏀寔Canvas.
return;
}
// if (this.isHidden()) return;
var currentBounds = this._map.getBounds();
if (!this.isadd && currentBounds.equals(this.bounds)) {
this.isadd = false;
return;
}
this.bounds = currentBounds;
var ne = this._map.lngLatToLayerPoint(currentBounds.getNorthEast()),
sw = this._map.lngLatToLayerPoint(currentBounds.getSouthWest()),
topY = ne.y,
leftX = sw.x,
h = sw.y - ne.y,
w = ne.x - sw.x;
this.conf.container.style.width = w + "px";
this.conf.container.style.height = h + "px";
this.conf.container.style[this.CSS_TRANSFORM()] =
"translate(" + Math.round(leftX) + "px," + Math.round(topY) + "px)";
if (this.latlngs.length > 0) {
this.heatmap.removeData();
}
var len = this.latlngs.length;
d = {
max: this.heatmap._store.getData().max,
data: [],
};
while (len--) {
var latlng = this.latlngs[len].latlng;
if (!currentBounds.contains(latlng)) {
continue;
}
var roundedPoint = this._getContainerPoint(latlng);
d.data.push({
x: roundedPoint.x,
y: roundedPoint.y,
count: this.latlngs[len].c,
});
}
this.heatmap.setData(d);
},
CSS_TRANSFORM: function () {
var div = document.createElement("div");
var props = [
"transform",
"WebkitTransform",
"MozTransform",
"OTransform",
"msTransform",
];
for (var i = 0; i < props.length; i++) {
var prop = props[i];
if (div.style[prop] !== undefined) {
return prop;
}
}
return props[0];
},
/**
* 璁剧疆鐑姏鍥惧睍鐜扮殑璇︾粏鏁版嵁, 瀹炵幇涔嬪悗,鍗冲彲浠ョ珛鍒诲睍鐜�
* {"<b>max</b>" : {Number} 鏉冮噸鐨勬渶澶у€�,
* <br />"<b>data</b>" : {Array} 鍧愭爣璇︾粏鏁版嵁,鏍煎紡濡備笅 <br/>
* {"lng":116.421969,"lat":39.913527,"count":3}, 鍏朵腑<br/>
* lng lat鍒嗗埆涓虹粡绾害, count鏉冮噸鍊�
* @param data
*/
setDataSet: function (data) {
this.data = data;
if (!this.isSupportCanvas()) {
//鍒ゆ柇鏄惁鏀寔Canvas.
return;
}
var currentBounds = this._map.getBounds();
var mapdata = {
max: data.max,
data: [],
};
var d = data.data,
dlen = d.length;
this.latlngs = [];
this.heatmap.removeData();
while (dlen--) {
var latlng = new T.LngLat(
d[dlen][this.conf.lngField],
d[dlen][this.conf.latField]
);
this.latlngs.push({
latlng: latlng,
c: d[dlen].count,
});
if (!currentBounds.contains(latlng)) {
continue;
}
var point = this._getContainerPoint(latlng);
mapdata.data.push({
x: point.x,
y: point.y,
count: d[dlen].count,
});
}
this.heatmap.setData(mapdata);
},
_getContainerPoint: function (latlng) {
var currentBounds = this._map.getBounds();
var divPixel = this._map.lngLatToLayerPoint(latlng),
leftX = this._map.lngLatToLayerPoint(currentBounds.getSouthWest()).x,
topY = this._map.lngLatToLayerPoint(currentBounds.getNorthEast()).y,
screenPixel = new T.Point(divPixel.x - leftX, divPixel.y - topY);
var point = this.pixelTransform(screenPixel);
return point;
},
/**
* 娣诲姞鍔垮姏鍥剧殑璇︾粏鍧愭爣鐐�
* @param {Number} lng 缁忓害鍧愭爣
* @param {Number} lat 缁忓害鍧愭爣
* @param {Number} count 缁忓害鍧愭爣
*/
addDataPoint: function (lng, lat, count) {
if (!this.isSupportCanvas()) {
return;
}
if (this.data && this.data.data) {
this.data.data.push({
lng: lng,
lat: lat,
count: count,
});
}
var latlng = new T.LngLat(lng, lat),
point = this.pixelTransform(this._map.lngLatToContainerPoint(latlng));
this.latlngs.push({
latlng: latlng,
c: count,
});
// this.heatmap.addData({x: point.x, y: point.y, value: count });
this.isadd = true;
this.draw();
},
/**
* 鍐呴儴浣跨敤鐨勫潗鏍囪浆鍖�
* @param p
* @returns {*}
*/
pixelTransform: function (p) {
var h = this.heatmap._config.container.clientHeight,
w = this.heatmap._config.container.clientWidth;
if (w == 0 || h == 0) return p;
while (p.x < 0) {
p.x += w;
}
while (p.x > w) {
p.x -= w;
}
while (p.y < 0) {
p.y += h;
}
while (p.y > h) {
p.y -= h;
}
p.x = p.x >> 0;
p.y = p.y >> 0;
return p;
},
/**
* 鏇存敼鐑姏鍥剧殑灞曠幇鎴栬€呭叧闂�
*/
toggle: function () {
if (!this.isSupportCanvas()) {
//鍒ゆ柇鏄惁鏀寔Canvas.
return;
}
if (this.conf.visible === true) {
this.conf.visible = false;
} else {
this.conf.visible = true;
}
if (this.conf.visible) {
this.conf.container.style.display = "block";
} else {
this.conf.container.style.display = "none";
}
return this.conf.visible;
},
setOptions: function (options) {
if (!this.isSupportCanvas()) {
//鍒ゆ柇鏄惁鏀寔Canvas.
return;
}
for (var key in options) {
if (key == "radius") {
this.heatmap._store._cfgRadius = options[key];
}
if (key == "opacity") {
options[key] = options[key] / 100;
}
}
this.heatmap.configure(options);
if (this.data) {
this.setDataSet(this.data); //閲嶆柊娓叉煋
}
},
isSupportCanvas: function () {
var elem = document.createElement("canvas");
return !!(elem.getContext && elem.getContext("2d"));
},
});
/*==============================浠ヤ笂閮ㄥ垎涓轰笓涓哄ぉ鍦板浘鎵撻€犵殑瑕嗙洊鐗�===================================================*/
/*==============================浠ヤ笅閮ㄥ垎涓篽eatmap.js鐨勬牳蹇冧唬鐮�,鍙礋璐g儹鍔涘浘鐨勫睍鐜�====================================*/
/*
* heatmap.js v2.0.2 | JavaScript Heatmap Library
*
* Copyright 2008-2016 Patrick Wied <heatmapjs@patrick-wied.at> - All rights reserved.
* Dual licensed under MIT and Beerware license
*
* :: 2016-02-04 21:41
*/
(function (name, context, factory) {
// Supports UMD. AMD, CommonJS/Node.js and browser context
if (typeof module !== "undefined" && module.exports) {
module.exports = factory();
} else if (typeof define === "function" && define.amd) {
define(factory);
} else {
context[name] = factory();
}
})("h337", this, function () {
// Heatmap Config stores default values and will be merged with instance config
var HeatmapConfig = {
defaultRadius: 40,
defaultRenderer: "canvas2d",
defaultGradient: {
0.25: "rgb(0,0,255)",
0.55: "rgb(0,255,0)",
0.85: "yellow",
1.0: "rgb(255,0,0)",
},
defaultMaxOpacity: 1,
defaultMinOpacity: 0,
defaultBlur: 0.85,
defaultXField: "x",
defaultYField: "y",
defaultValueField: "value",
plugins: {},
};
var Store = (function StoreClosure() {
var Store = function Store(config) {
this._coordinator = {};
this._data = [];
this._radi = [];
this._min = 0;
this._max = 1;
this._xField = config["xField"] || config.defaultXField;
this._yField = config["yField"] || config.defaultYField;
this._valueField = config["valueField"] || config.defaultValueField;
if (config["radius"]) {
this._cfgRadius = config["radius"];
}
};
var defaultRadius = HeatmapConfig.defaultRadius;
Store.prototype = {
// when forceRender = false -> called from setData, omits renderall event
_organiseData: function (dataPoint, forceRender) {
var x = dataPoint[this._xField];
var y = dataPoint[this._yField];
var radi = this._radi;
var store = this._data;
var max = this._max;
var min = this._min;
var value = dataPoint[this._valueField] || 1;
var radius = dataPoint.radius || this._cfgRadius || defaultRadius;
if (!store[x]) {
store[x] = [];
radi[x] = [];
}
if (!store[x][y]) {
store[x][y] = value;
radi[x][y] = radius;
} else {
store[x][y] += value;
}
if (store[x][y] > max) {
if (!forceRender) {
this._max = store[x][y];
} else {
this.setDataMax(store[x][y]);
}
return false;
} else {
return {
x: x,
y: y,
value: value,
radius: radius,
min: min,
max: max,
};
}
},
_unOrganizeData: function () {
var unorganizedData = [];
var data = this._data;
var radi = this._radi;
for (var x in data) {
for (var y in data[x]) {
unorganizedData.push({
x: x,
y: y,
radius: radi[x][y],
value: data[x][y],
});
}
}
return {
min: this._min,
max: this._max,
data: unorganizedData,
};
},
_onExtremaChange: function () {
this._coordinator.emit("extremachange", {
min: this._min,
max: this._max,
});
},
addData: function () {
if (arguments[0].length > 0) {
var dataArr = arguments[0];
var dataLen = dataArr.length;
while (dataLen--) {
this.addData.call(this, dataArr[dataLen]);
}
} else {
// add to store
var organisedEntry = this._organiseData(arguments[0], true);
if (organisedEntry) {
this._coordinator.emit("renderpartial", {
min: this._min,
max: this._max,
data: [organisedEntry],
});
}
}
return this;
},
setData: function (data) {
var dataPoints = data.data;
var pointsLen = dataPoints.length;
// reset data arrays
this._data = [];
this._radi = [];
for (var i = 0; i < pointsLen; i++) {
this._organiseData(dataPoints[i], false);
}
this._max = data.max;
this._min = data.min || 0;
this._onExtremaChange();
this._coordinator.emit("renderall", this._getInternalData());
return this;
},
removeData: function () {
// TODO: implement
},
setDataMax: function (max) {
this._max = max;
this._onExtremaChange();
this._coordinator.emit("renderall", this._getInternalData());
return this;
},
setDataMin: function (min) {
this._min = min;
this._onExtremaChange();
this._coordinator.emit("renderall", this._getInternalData());
return this;
},
setCoordinator: function (coordinator) {
this._coordinator = coordinator;
},
_getInternalData: function () {
return {
max: this._max,
min: this._min,
data: this._data,
radi: this._radi,
};
},
getData: function () {
return this._unOrganizeData();
} /*,
TODO: rethink.
getValueAt: function(point) {
var value;
var radius = 100;
var x = point.x;
var y = point.y;
var data = this._data;
if (data[x] && data[x][y]) {
return data[x][y];
} else {
var values = [];
// radial search for datapoints based on default radius
for(var distance = 1; distance < radius; distance++) {
var neighbors = distance * 2 +1;
var startX = x - distance;
var startY = y - distance;
for(var i = 0; i < neighbors; i++) {
for (var o = 0; o < neighbors; o++) {
if ((i == 0 || i == neighbors-1) || (o == 0 || o == neighbors-1)) {
if (data[startY+i] && data[startY+i][startX+o]) {
values.push(data[startY+i][startX+o]);
}
} else {
continue;
}
}
}
}
if (values.length > 0) {
return Math.max.apply(Math, values);
}
}
return false;
}*/,
};
return Store;
})();
var Canvas2dRenderer = (function Canvas2dRendererClosure() {
var _getColorPalette = function (config) {
var gradientConfig = config.gradient || config.defaultGradient;
var paletteCanvas = document.createElement("canvas");
var paletteCtx = paletteCanvas.getContext("2d");
paletteCanvas.width = 256;
paletteCanvas.height = 1;
var gradient = paletteCtx.createLinearGradient(0, 0, 256, 1);
for (var key in gradientConfig) {
gradient.addColorStop(key, gradientConfig[key]);
}
paletteCtx.fillStyle = gradient;
paletteCtx.fillRect(0, 0, 256, 1);
return paletteCtx.getImageData(0, 0, 256, 1).data;
};
var _getPointTemplate = function (radius, blurFactor) {
var tplCanvas = document.createElement("canvas");
var tplCtx = tplCanvas.getContext("2d");
var x = radius;
var y = radius;
tplCanvas.width = tplCanvas.height = radius * 2;
if (blurFactor == 1) {
tplCtx.beginPath();
tplCtx.arc(x, y, radius, 0, 2 * Math.PI, false);
tplCtx.fillStyle = "rgba(0,0,0,1)";
tplCtx.fill();
} else {
var gradient = tplCtx.createRadialGradient(
x,
y,
radius * blurFactor,
x,
y,
radius
);
gradient.addColorStop(0, "rgba(0,0,0,1)");
gradient.addColorStop(1, "rgba(0,0,0,0)");
tplCtx.fillStyle = gradient;
tplCtx.fillRect(0, 0, 2 * radius, 2 * radius);
}
return tplCanvas;
};
var _prepareData = function (data) {
var renderData = [];
var min = data.min;
var max = data.max;
var radi = data.radi;
var data = data.data;
var xValues = Object.keys(data);
var xValuesLen = xValues.length;
while (xValuesLen--) {
var xValue = xValues[xValuesLen];
var yValues = Object.keys(data[xValue]);
var yValuesLen = yValues.length;
while (yValuesLen--) {
var yValue = yValues[yValuesLen];
var value = data[xValue][yValue];
var radius = radi[xValue][yValue];
renderData.push({
x: xValue,
y: yValue,
value: value,
radius: radius,
});
}
}
return {
min: min,
max: max,
data: renderData,
};
};
function Canvas2dRenderer(config) {
var container = config.container;
var shadowCanvas = (this.shadowCanvas = document.createElement("canvas"));
var canvas = (this.canvas =
config.canvas || document.createElement("canvas"));
var renderBoundaries = (this._renderBoundaries = [10000, 10000, 0, 0]);
var computed = getComputedStyle(config.container) || {};
canvas.className = "heatmap-canvas";
this._width =
canvas.width =
shadowCanvas.width =
config.width || +computed.width.replace(/px/, "");
this._height =
canvas.height =
shadowCanvas.height =
config.height || +computed.height.replace(/px/, "");
this.shadowCtx = shadowCanvas.getContext("2d");
this.ctx = canvas.getContext("2d");
// @TODO:
// conditional wrapper
canvas.style.cssText = shadowCanvas.style.cssText =
"position:absolute;left:0;top:0;";
container.style.position = "relative";
container.appendChild(canvas);
this._palette = _getColorPalette(config);
this._templates = {};
this._setStyles(config);
}
Canvas2dRenderer.prototype = {
renderPartial: function (data) {
if (data.data.length > 0) {
this._drawAlpha(data);
this._colorize();
}
},
renderAll: function (data) {
// reset render boundaries
this._clear();
if (data.data.length > 0) {
this._drawAlpha(_prepareData(data));
this._colorize();
}
},
_updateGradient: function (config) {
this._palette = _getColorPalette(config);
},
updateConfig: function (config) {
if (config["gradient"]) {
this._updateGradient(config);
}
this._setStyles(config);
},
setDimensions: function (width, height) {
this._width = width;
this._height = height;
this.canvas.width = this.shadowCanvas.width = width;
this.canvas.height = this.shadowCanvas.height = height;
},
_clear: function () {
this.shadowCtx.clearRect(0, 0, this._width, this._height);
this.ctx.clearRect(0, 0, this._width, this._height);
},
_setStyles: function (config) {
this._blur = config.blur == 0 ? 0 : config.blur || config.defaultBlur;
if (config.backgroundColor) {
this.canvas.style.backgroundColor = config.backgroundColor;
}
this._width =
this.canvas.width =
this.shadowCanvas.width =
config.width || this._width;
this._height =
this.canvas.height =
this.shadowCanvas.height =
config.height || this._height;
this._opacity = (config.opacity || 0) * 255;
this._maxOpacity =
(config.maxOpacity || config.defaultMaxOpacity) * 255;
this._minOpacity =
(config.minOpacity || config.defaultMinOpacity) * 255;
this._useGradientOpacity = !!config.useGradientOpacity;
},
_drawAlpha: function (data) {
var min = (this._min = data.min);
var max = (this._max = data.max);
var data = data.data || [];
var dataLen = data.length;
// on a point basis?
var blur = 1 - this._blur;
while (dataLen--) {
var point = data[dataLen];
var x = point.x;
var y = point.y;
var radius = point.radius;
// if value is bigger than max
// use max as value
var value = Math.min(point.value, max);
var rectX = x - radius;
var rectY = y - radius;
var shadowCtx = this.shadowCtx;
var tpl;
if (!this._templates[radius]) {
this._templates[radius] = tpl = _getPointTemplate(radius, blur);
} else {
tpl = this._templates[radius];
}
// value from minimum / value range
// => [0, 1]
var templateAlpha = (value - min) / (max - min);
// this fixes #176: small values are not visible because globalAlpha < .01 cannot be read from imageData
shadowCtx.globalAlpha = templateAlpha < 0.01 ? 0.01 : templateAlpha;
shadowCtx.drawImage(tpl, rectX, rectY);
// update renderBoundaries
if (rectX < this._renderBoundaries[0]) {
this._renderBoundaries[0] = rectX;
}
if (rectY < this._renderBoundaries[1]) {
this._renderBoundaries[1] = rectY;
}
if (rectX + 2 * radius > this._renderBoundaries[2]) {
this._renderBoundaries[2] = rectX + 2 * radius;
}
if (rectY + 2 * radius > this._renderBoundaries[3]) {
this._renderBoundaries[3] = rectY + 2 * radius;
}
}
},
_colorize: function () {
var x = this._renderBoundaries[0];
var y = this._renderBoundaries[1];
var width = this._renderBoundaries[2] - x;
var height = this._renderBoundaries[3] - y;
var maxWidth = this._width;
var maxHeight = this._height;
var opacity = this._opacity;
var maxOpacity = this._maxOpacity;
var minOpacity = this._minOpacity;
var useGradientOpacity = this._useGradientOpacity;
if (x < 0) {
x = 0;
}
if (y < 0) {
y = 0;
}
if (x + width > maxWidth) {
width = maxWidth - x;
}
if (y + height > maxHeight) {
height = maxHeight - y;
}
var img = this.shadowCtx.getImageData(x, y, width, height);
var imgData = img.data;
var len = imgData.length;
var palette = this._palette;
for (var i = 3; i < len; i += 4) {
var alpha = imgData[i];
var offset = alpha * 4;
if (!offset) {
continue;
}
var finalAlpha;
if (opacity > 0) {
finalAlpha = opacity;
} else {
if (alpha < maxOpacity) {
if (alpha < minOpacity) {
finalAlpha = minOpacity;
} else {
finalAlpha = alpha;
}
} else {
finalAlpha = maxOpacity;
}
}
imgData[i - 3] = palette[offset];
imgData[i - 2] = palette[offset + 1];
imgData[i - 1] = palette[offset + 2];
imgData[i] = useGradientOpacity ? palette[offset + 3] : finalAlpha;
}
img.data = imgData;
this.ctx.putImageData(img, x, y);
this._renderBoundaries = [1000, 1000, 0, 0];
},
getValueAt: function (point) {
var value;
var shadowCtx = this.shadowCtx;
var img = shadowCtx.getImageData(point.x, point.y, 1, 1);
var data = img.data[3];
var max = this._max;
var min = this._min;
value = (Math.abs(max - min) * (data / 255)) >> 0;
return value;
},
getDataURL: function () {
return this.canvas.toDataURL();
},
};
return Canvas2dRenderer;
})();
var Renderer = (function RendererClosure() {
var rendererFn = false;
if (HeatmapConfig["defaultRenderer"] === "canvas2d") {
rendererFn = Canvas2dRenderer;
}
return rendererFn;
})();
var Util = {
merge: function () {
var merged = {};
var argsLen = arguments.length;
for (var i = 0; i < argsLen; i++) {
var obj = arguments[i];
for (var key in obj) {
merged[key] = obj[key];
}
}
return merged;
},
};
// Heatmap Constructor
var Heatmap = (function HeatmapClosure() {
var Coordinator = (function CoordinatorClosure() {
function Coordinator() {
this.cStore = {};
}
Coordinator.prototype = {
on: function (evtName, callback, scope) {
var cStore = this.cStore;
if (!cStore[evtName]) {
cStore[evtName] = [];
}
cStore[evtName].push(function (data) {
return callback.call(scope, data);
});
},
emit: function (evtName, data) {
var cStore = this.cStore;
if (cStore[evtName]) {
var len = cStore[evtName].length;
for (var i = 0; i < len; i++) {
var callback = cStore[evtName][i];
callback(data);
}
}
},
};
return Coordinator;
})();
var _connect = function (scope) {
var renderer = scope._renderer;
var coordinator = scope._coordinator;
var store = scope._store;
coordinator.on("renderpartial", renderer.renderPartial, renderer);
coordinator.on("renderall", renderer.renderAll, renderer);
coordinator.on("extremachange", function (data) {
scope._config.onExtremaChange &&
scope._config.onExtremaChange({
min: data.min,
max: data.max,
gradient:
scope._config["gradient"] || scope._config["defaultGradient"],
});
});
store.setCoordinator(coordinator);
};
function Heatmap() {
var config = (this._config = Util.merge(
HeatmapConfig,
arguments[0] || {}
));
this._coordinator = new Coordinator();
if (config["plugin"]) {
var pluginToLoad = config["plugin"];
if (!HeatmapConfig.plugins[pluginToLoad]) {
throw new Error(
"Plugin '" +
pluginToLoad +
"' not found. Maybe it was not registered."
);
} else {
var plugin = HeatmapConfig.plugins[pluginToLoad];
// set plugin renderer and store
this._renderer = new plugin.renderer(config);
this._store = new plugin.store(config);
}
} else {
this._renderer = new Renderer(config);
this._store = new Store(config);
}
_connect(this);
}
// @TODO:
// add API documentation
Heatmap.prototype = {
addData: function () {
this._store.addData.apply(this._store, arguments);
return this;
},
removeData: function () {
this._store.removeData &&
this._store.removeData.apply(this._store, arguments);
return this;
},
setData: function () {
this._store.setData.apply(this._store, arguments);
return this;
},
setDataMax: function () {
this._store.setDataMax.apply(this._store, arguments);
return this;
},
setDataMin: function () {
this._store.setDataMin.apply(this._store, arguments);
return this;
},
configure: function (config) {
this._config = Util.merge(this._config, config);
this._renderer.updateConfig(this._config);
this._coordinator.emit("renderall", this._store._getInternalData());
return this;
},
repaint: function () {
this._coordinator.emit("renderall", this._store._getInternalData());
return this;
},
getData: function () {
return this._store.getData();
},
getDataURL: function () {
return this._renderer.getDataURL();
},
getValueAt: function (point) {
if (this._store.getValueAt) {
return this._store.getValueAt(point);
} else if (this._renderer.getValueAt) {
return this._renderer.getValueAt(point);
} else {
return null;
}
},
};
return Heatmap;
})();
// core
var heatmapFactory = {
create: function (config) {
return new Heatmap(config);
},
register: function (pluginKey, plugin) {
HeatmapConfig.plugins[pluginKey] = plugin;
},
};
return heatmapFactory;
});