
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
import { MapPointSeries } from "./MapPointSeries";
import { DataItem } from "../../core/render/Component";
import { Container } from "../../core/render/Container";
import { Label } from "../../core/render/Label";
import * as $array from "../../core/util/Array";
import * as $object from "../../core/util/Object";
import * as d3hierarchy from "d3-hierarchy";
import * as $math from "../../core/util/Math";
/**
* A version of [[MapPointSeries]] which can automatically group closely located
* bullets into groups.
*
* @see {@link https://www.amcharts.com/docs/v5/charts/map-chart/clustered-point-series/} for more info
* @since 5.5.6
* @important
*/
export class ClusteredPointSeries extends MapPointSeries {
constructor() {
super(...arguments);
Object.defineProperty(this, "_dataItem", {
enumerable: true,
configurable: true,
writable: true,
value: this.makeDataItem({})
});
Object.defineProperty(this, "_clusterIndex", {
enumerable: true,
configurable: true,
writable: true,
value: 0
});
Object.defineProperty(this, "_clusters", {
enumerable: true,
configurable: true,
writable: true,
value: []
});
Object.defineProperty(this, "clusteredDataItems", {
enumerable: true,
configurable: true,
writable: true,
value: []
});
Object.defineProperty(this, "_scatterIndex", {
enumerable: true,
configurable: true,
writable: true,
value: 0
});
Object.defineProperty(this, "_scatters", {
enumerable: true,
configurable: true,
writable: true,
value: []
});
Object.defineProperty(this, "_packLayout", {
enumerable: true,
configurable: true,
writable: true,
value: d3hierarchy.pack()
});
Object.defineProperty(this, "_spiral", {
enumerable: true,
configurable: true,
writable: true,
value: []
});
}
_afterNew() {
this.fields.push("groupId");
this._setRawDefault("groupIdField", "groupId");
super._afterNew();
}
_updateChildren() {
super._updateChildren();
if (this.isDirty("scatterRadius")) {
this._spiral = $math.spiralPoints(0, 0, 300, 300, 0, 3, 3, 0, 0);
}
const groups = {};
// distribute to groups
$array.each(this.dataItems, (dataItem) => {
const groupId = dataItem.get("groupId", "_default");
if (!groups[groupId]) {
groups[groupId] = [];
}
groups[groupId].push(dataItem);
});
this._scatterIndex = -1;
this._scatters = [];
this._clusterIndex = -1;
this._clusters = [];
$array.each(this.clusteredDataItems, (dataItem) => {
dataItem.setRaw("children", undefined);
});
$array.each(this.dataItems, (dataItem) => {
dataItem.setRaw("cluster", undefined);
});
$object.each(groups, (_key, group) => {
this._scatterGroup(group);
});
$object.each(groups, (_key, group) => {
this._clusterGroup(group);
});
$array.each(this.dataItems, (dataItem) => {
if (!dataItem.get("cluster")) {
const bullets = dataItem.bullets;
if (bullets) {
$array.each(bullets, (bullet) => {
const sprite = bullet.get("sprite");
if (sprite) {
sprite.set("forceHidden", false);
}
});
}
}
});
}
/**
* Zooms to the area so that all clustered data items of a cluster would be
* visible.
*
* Pass in `true` as a second parameter to rotate that map so that the group
* is in the center. This is especially useful in the maps that use
* Orthographic (globe) projection.
*
* @param dataItem Group data item
* @param rotate Rotate the map so that group is in the center?
* @see {@link https://www.amcharts.com/docs/v5/charts/map-chart/clustered-point-series/#Drill_down} for more info
*/
zoomToCluster(dataItem, rotate) {
this.zoomToDataItems(dataItem.get("children", []), rotate);
}
_clusterGroup(dataItems) {
const chart = this.chart;
if (chart && chart.get("zoomLevel", 1) >= chart.get("maxZoomLevel", 100) * this.get("stopClusterZoom", 0.95)) {
// void
}
else {
while (dataItems.length > 0) {
this._clusterIndex++;
this._clusters[this._clusterIndex] = [];
const cluster = this._clusters[this._clusterIndex];
const dataItem = dataItems[0];
cluster.push(dataItem);
$array.remove(dataItems, dataItem);
this._clusterDataItem(dataItem, dataItems);
}
}
let i = 0;
$array.each(this._clusters, (cluster) => {
let sumX = 0;
let sumY = 0;
let len = cluster.length;
if (len > 1) {
let clusteredDataItem = this.clusteredDataItems[i];
if (!clusteredDataItem) {
clusteredDataItem = new DataItem(this, undefined, {});
const bulletMethod = this.get("clusteredBullet");
if (bulletMethod) {
const bullet = clusteredDataItem.set("bullet", bulletMethod(this._root, this, clusteredDataItem));
if (bullet) {
const sprite = bullet.get("sprite");
if (sprite) {
this.bulletsContainer.children.push(sprite);
sprite._setDataItem(clusteredDataItem);
}
}
}
this.clusteredDataItems.push(clusteredDataItem);
}
$array.each(cluster, (dataItem) => {
dataItem.setRaw("cluster", clusteredDataItem);
const point = dataItem.get("point");
if (point) {
sumX += point.x;
sumY += point.y;
}
const bullets = dataItem.bullets;
if (bullets) {
$array.each(bullets, (bullet) => {
const sprite = bullet.get("sprite");
if (sprite) {
sprite.set("forceHidden", true);
}
});
}
});
let averageX = sumX / len;
let averageY = sumY / len;
clusteredDataItem.setRaw("children", cluster);
const prevLen = clusteredDataItem.get("value");
clusteredDataItem.setRaw("value", len);
const bullet = clusteredDataItem.get("bullet");
if (bullet) {
const sprite = bullet.get("sprite");
if (sprite) {
sprite.set("forceHidden", false);
sprite.setAll({ x: averageX, y: averageY });
if (prevLen != len) {
if (sprite instanceof Container) {
sprite.walkChildren((child) => {
if (child instanceof Label) {
child.text.markDirtyText();
}
});
}
}
}
}
i++;
}
});
$array.each(this.clusteredDataItems, (dataItem) => {
let children = dataItem.get("children");
if (!children || children.length == 0) {
const bullet = dataItem.get("bullet");
if (bullet) {
const sprite = bullet.get("sprite");
if (sprite) {
sprite.set("forceHidden", true);
}
}
}
});
}
_onDataClear() {
super._onDataClear();
$array.each(this.clusteredDataItems, (dataItem) => {
const bullet = dataItem.get("bullet");
if (bullet) {
const sprite = bullet.get("sprite");
if (sprite) {
sprite.dispose();
}
}
});
this.clusteredDataItems = [];
}
_clusterDataItem(dataItem, dataItems) {
const point = dataItem.get("point");
if (point) {
$array.each(dataItems, (di) => {
if (di && !di.get("clipped")) {
const diPoint = di.get("point");
if (diPoint) {
if (Math.hypot(diPoint.x - point.x, diPoint.y - point.y) < this.get("minDistance", 20)) {
const cluster = this._clusters[this._clusterIndex];
cluster.push(di);
$array.remove(dataItems, di);
this._clusterDataItem(di, dataItems);
}
}
}
});
}
}
_scatterGroup(dataItems) {
const chart = this.chart;
if (chart && chart.get("zoomLevel", 1) >= chart.get("maxZoomLevel", 100) * this.get("stopClusterZoom", 0.95)) {
while (dataItems.length > 0) {
this._scatterIndex++;
this._scatters[this._scatterIndex] = [];
const scatter = this._scatters[this._scatterIndex];
const dataItem = dataItems[0];
scatter.push(dataItem);
$array.remove(dataItems, dataItem);
this._scatterDataItem(dataItem, dataItems);
}
$array.each(this._scatters, (scatter) => {
let len = scatter.length;
if (len > 1) {
let previousCircles = [];
let s = 0;
let radius = this.get("scatterRadius", 8);
$array.each(scatter, (dataItem) => {
let spiralPoint = this._spiral[s];
let intersects = true;
if (previousCircles.length > 0) {
while (intersects) {
$array.each(previousCircles, (previousCircle) => {
intersects = false;
while ($math.circlesOverlap({ x: spiralPoint.x, y: spiralPoint.y, radius: radius }, previousCircle)) {
s++;
if (this._spiral[s] == undefined) {
intersects = false;
}
else {
intersects = true;
spiralPoint = this._spiral[s];
}
}
});
}
}
const dx = spiralPoint.x;
const dy = spiralPoint.y;
previousCircles.push({ x: dx, y: dy, radius: radius });
dataItem.set("dx", dx);
dataItem.set("dy", dy);
const bullets = dataItem.bullets;
if (bullets) {
$array.each(bullets, (bullet) => {
const sprite = bullet.get("sprite");
if (sprite) {
sprite.setAll({ dx: dx, dy: dy });
}
});
}
});
}
});
}
}
_scatterDataItem(dataItem, dataItems) {
const point = dataItem.get("point");
if (point) {
$array.each(dataItems, (di) => {
if (di && !di.get("clipped")) {
const diPoint = di.get("point");
if (diPoint) {
if (Math.hypot(diPoint.x - point.x, diPoint.y - point.y) < this.get("scatterDistance", 5)) {
const scatter = this._scatters[this._scatterIndex];
scatter.push(di);
$array.remove(dataItems, di);
this._scatterDataItem(di, dataItems);
}
}
}
});
}
}
}
Object.defineProperty(ClusteredPointSeries, "className", {
enumerable: true,
configurable: true,
writable: true,
value: "ClusteredPointSeries"
});
Object.defineProperty(ClusteredPointSeries, "classNames", {
enumerable: true,
configurable: true,
writable: true,
value: MapPointSeries.classNames.concat([ClusteredPointSeries.className])
});
//# sourceMappingURL=ClusteredPointSeries.js.map