使用的插件是FlareClusterLayer
注意: 引入插件需要使用絕對地址
插件案例
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<title>Flare Cluster Layer Example</title>
<link href="//fonts.googleapis.com/css?family=Abel" rel="stylesheet">
<link rel="stylesheet" href="https://js.arcgis.com/4.12/esri/css/main.css">
<style>
html, body {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
font-family: abel, Arial, Calibri;
overflow: hidden;
}
#container, .view {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
position: relative;
}
.view.hidden {
position: absolute;
z-index: -10;
}
/* top menu */
#top-menu {
-webkit-box-shadow: 2px 3px 22px 0px rgba(173, 173, 173, 0.75);
-moz-box-shadow: 2px 3px 22px 0px rgba(173, 173, 173, 0.75);
box-shadow: 2px 3px 22px 0px rgba(173, 173, 173, 0.75);
position: absolute;
left: 10px;
top: 10px;
padding: 0 10px;
height: 45px;
width: auto;
background: rgba(46, 68, 94, 0.8);
z-index: 2001;
color: #FFF;
}
#top-menu .section {
display: inline-block;
height: 100%;
padding: 0 5px;
}
#top-menu .separator {
height: 100%;
width: 3px;
border-right: 1px solid #d0d0d0;
padding: 0;
}
#top-menu .button {
display: inline-block;
height: 100%;
color: #FFF;
margin: 0 5px;
background: none;
border: none;
cursor: pointer;
width: 85px;
padding: 0;
}
#top-menu .button.active {
font-weight: bold;
background: rgba(0,0,0,0.1);
border: 1px solid rgba(255,255,255,0.1);
}
#top-menu .button:focus {
outline: none;
}
#top-menu .button:hover {
opacity: 0.7;
transition: opacity 0.4s;
}
#top-menu select {
font-size: 1em;
height: 60%;
font-family: abel, Arial;
}
@media (min-width: 320px) and (max-width: 639px) {
#top-menu #title-text {
display: none;
}
#top-menu .button {
width: 75px;
margin: 0;
}
}
#api-link {
position: absolute;
bottom: 20px;
left: 10px;
font-weight: bold;
}
/* Set up some css rules to animate things */
/* Some rules to change the appearance of clusters and it's text when activated */
/* Scale up the clusters when activated */
.cluster-group.activated {
transform-origin: center;
transform: scale(1.2);
transition: transform linear 0.4s;
}
/* Change the appearance of clusters when activated */
.cluster-group.activated .cluster {
stroke: rgba(255,255,255,1);
stroke-width: 2;
transition: all ease 1s;
}
.cluster-group.activated .cluster-text {
fill: #000;
font-weight: bold;
transition: all ease 1s;
}
/* hide flares by default */
.flare-group {
opacity: 0;
}
/* animate display of flares */
.flare-group.activated {
opacity: 1;
transition: opacity linear 0.06s;
}
/* this just chains the display of flares to occur one after the other using transition delay - could be a better way to do this natively but using SASS or LESS this would be much more concise */
.flare-group.activated:nth-of-type(1) {
transition-delay: 0.06s;
}
.flare-group.activated:nth-of-type(2) {
transition-delay: 0.12s;
}
.flare-group.activated:nth-of-type(3) {
transition-delay: 0.18s;
}
.flare-group.activated:nth-of-type(4) {
transition-delay: 0.24s;
}
.flare-group.activated:nth-of-type(5) {
transition-delay: 0.30s;
}
.flare-group.activated:nth-of-type(6) {
transition-delay: 0.36s;
}
.cluster-group .flare-group.activated:nth-of-type(7) {
transition-delay: 0.42s;
}
.flare-group.activated:nth-of-type(8) {
transition-delay: 0.48s;
}
/*
Cross browser notes on the example CSS animations:
IE/Edge: These POS's don't support transforms on svg elements using css, so the css transform animations won't work.
*/
</style>
<script>
var dojoConfig = {
async: true,
tlmSiblingOfDojo: false,
packages: [{
name: "fcl",
location: location.pathname.replace(/\/[^/]+$/, '') + "/fcl"
}],
has: {
"esri-promise-compatibility": 1 // enable native promises and remove the warning about using when() instead of then(). https://developers.arcgis.com/javascript/latest/guide/release-notes/index.html#improved-compatibility-with-javascript-promises
}
};
</script>
<script src="https://js.arcgis.com/4.12/"></script>
<script type="text/javascript">
var map,
mapView,
sceneView,
activeView,
graphics,
clusterLayer;
//set some defaults
var maxSingleFlareCount = 8;
var areaDisplayMode = "activated";
require(["esri/Map",
"esri/Color",
"esri/views/MapView",
"esri/views/SceneView",
"esri/geometry/Extent",
"esri/geometry/SpatialReference",
"esri/geometry/Point",
"esri/PopupTemplate",
"esri/layers/GraphicsLayer",
"esri/Graphic",
"esri/symbols/SimpleMarkerSymbol",
"esri/symbols/SimpleLineSymbol",
"esri/symbols/SimpleFillSymbol",
"esri/symbols/TextSymbol",
"esri/symbols/TextSymbol3DLayer",
"esri/symbols/Font",
"esri/renderers/ClassBreaksRenderer",
"esri/core/watchUtils",
"esri/geometry/support/webMercatorUtils",
"fcl/FlareClusterLayer_v4",
"dojo",
"dojo/ready",
"dojo/json",
"dojo/dom",
"dojo/on",
"dojo/dom-attr",
"dojo/dom-class",
"dojo/text!./lib/data.json"],
function (Map, Color, MapView, SceneView, Extent, SpatialReference, Point, PopupTemplate, GraphicsLayer, Graphic, SimpleMarkerSymbol, SimpleLineSymbol, SimpleFillSymbol, TextSymbol, TextSymbol3DLayer, Font, ClassBreaksRenderer,
watchUtils, webMercatorUtils, fcl, dojo, ready, JSON, dom, on, domAttr, domClass, data) {
ready(function () {
document.getElementById("area-mode").value = areaDisplayMode;
map = new Map({
basemap: "gray"
});
mapView = new MapView({
map: map,
container: "views",
center: [133, -25],
zoom: 4,
constraints: { minZoom: 3 },
ui: { components: ["compass", "zoom"] }
});
mapView.ui.move("zoom", "bottom-right");
mapView.ui.move("compass", "bottom-right");
// default map view to be active
activeView = mapView;
setActiveViewClasses(activeView);
sceneView = new SceneView({
map: map,
center: [133, -25],
zoom: 4,
ui: { components: ["compass", "zoom"] }
});
sceneView.ui.move("zoom", "bottom-right");
sceneView.ui.move("compass", "bottom-right");
//load up the dummy data
var allData = JSON.parse(data);
mapView.when(function (e) {
console.log('map view loaded');
if (activeView === mapView) {
initLayer(allData);
}
}, function (err) {
console.error("failed to load MapView " + e);
});
});
function initLayer(data) {
//init the layer, more options are available and explained in the cluster layer constructor
//set up a class breaks renderer to render different symbols based on the cluster count. Use the required clusterCount property to break on.
var defaultSym = new SimpleMarkerSymbol({
size: 6,
color: "#FF0000",
outline: null
});
var renderer = new ClassBreaksRenderer({
defaultSymbol: defaultSym
});
renderer.field = "clusterCount";
var smSymbol = new SimpleMarkerSymbol({ size: 22, outline: new SimpleLineSymbol({ color: [221, 159, 34, 0.8] }), color: [255, 204, 102, 0.8] });
var mdSymbol = new SimpleMarkerSymbol({ size: 24, outline: new SimpleLineSymbol({ color: [82, 163, 204, 0.8] }), color: [102, 204, 255, 0.8] });
var lgSymbol = new SimpleMarkerSymbol({ size: 28, outline: new SimpleLineSymbol({ color: [41, 163, 41, 0.8] }), color: [51, 204, 51, 0.8] });
var xlSymbol = new SimpleMarkerSymbol({ size: 32, outline: new SimpleLineSymbol({ color: [200, 52, 59, 0.8] }), color: [250, 65, 74, 0.8] });
renderer.addClassBreakInfo(0, 19, smSymbol);
renderer.addClassBreakInfo(20, 150, mdSymbol);
renderer.addClassBreakInfo(151, 1000, lgSymbol);
renderer.addClassBreakInfo(1001, Infinity, xlSymbol);
var areaRenderer;
// if area display mode is set. Create a renderer to display cluster areas. Use SimpleFillSymbols as the areas are polygons
var defaultAreaSym = new SimpleFillSymbol({
style: "solid",
color: [0, 0, 0, 0.2],
outline: new SimpleLineSymbol({ color: [0, 0, 0, 0.3] })
});
areaRenderer = new ClassBreaksRenderer({
defaultSymbol: defaultAreaSym
});
areaRenderer.field = "clusterCount";
var smAreaSymbol = new SimpleFillSymbol({ color: [255, 204, 102, 0.4], outline: new SimpleLineSymbol({ color: [221, 159, 34, 0.8], style: "dash" }) });
var mdAreaSymbol = new SimpleFillSymbol({ color: [102, 204, 255, 0.4], outline: new SimpleLineSymbol({ color: [82, 163, 204, 0.8], style: "dash" }) });
var lgAreaSymbol = new SimpleFillSymbol({ color: [51, 204, 51, 0.4], outline: new SimpleLineSymbol({ color: [41, 163, 41, 0.8], style: "dash" }) });
var xlAreaSymbol = new SimpleFillSymbol({ color: [250, 65, 74, 0.4], outline: new SimpleLineSymbol({ color: [200, 52, 59, 0.8], style: "dash" }) });
areaRenderer.addClassBreakInfo(0, 19, smAreaSymbol);
areaRenderer.addClassBreakInfo(20, 150, mdAreaSymbol);
areaRenderer.addClassBreakInfo(151, 1000, lgAreaSymbol);
areaRenderer.addClassBreakInfo(1001, Infinity, xlAreaSymbol);
// Set up another class breaks renderer to style the flares individually
var flareRenderer = new ClassBreaksRenderer({
defaultSymbol: renderer.defaultSymbol
});
flareRenderer.field = "clusterCount";
var smFlareSymbol = new SimpleMarkerSymbol({ size: 14, color: [255, 204, 102, 0.8], outline: new SimpleLineSymbol({ color: [221, 159, 34, 0.8] }) });
var mdFlareSymbol = new SimpleMarkerSymbol({ size: 14, color: [102, 204, 255, 0.8], outline: new SimpleLineSymbol({ color: [82, 163, 204, 0.8] }) });
var lgFlareSymbol = new SimpleMarkerSymbol({ size: 14, color: [51, 204, 51, 0.8], outline: new SimpleLineSymbol({ color: [41, 163, 41, 0.8] }) });
var xlFlareSymbol = new SimpleMarkerSymbol({ size: 14, color: [250, 65, 74, 0.8], outline: new SimpleLineSymbol({ color: [200, 52, 59, 0.8] }) });
flareRenderer.addClassBreakInfo(0, 19, smFlareSymbol);
flareRenderer.addClassBreakInfo(20, 150, mdFlareSymbol);
flareRenderer.addClassBreakInfo(151, 1000, lgFlareSymbol);
flareRenderer.addClassBreakInfo(1001, Infinity, xlFlareSymbol);
// set up a popup template
var popupTemplate = new PopupTemplate({
title: "{name}",
content: [{
type: "fields",
fieldInfos: [
{ fieldName: "facilityType", label: "Facility Type", visible: true },
{ fieldName: "postcode", label: "Post Code", visible: true },
{ fieldName: "isOpen", label: "Opening Hours", visible: true }
]
}]
});
var options = {
id: "flare-cluster-layer",
clusterRenderer: renderer,
areaRenderer: areaRenderer,
flareRenderer: flareRenderer,
singlePopupTemplate: popupTemplate,
spatialReference: new SpatialReference({ "wkid": 4326 }),
subTypeFlareProperty: "facilityType",
singleFlareTooltipProperty: "name",
displaySubTypeFlares: true,
maxSingleFlareCount: maxSingleFlareCount,
clusterRatio: 75,
clusterAreaDisplay: areaDisplayMode,
data: data
}
clusterLayer = new fcl.FlareClusterLayer(options);
map.add(clusterLayer);
clusterLayer.on("draw-complete", function () {
//console.log('draw complete event callback');
});
}
function clearLayer() {
map.remove(clusterLayer);
clusterLayer = null;
}
//setup some event handlers to react to change of options
on(dom.byId("map-view-select"), "click", function (evt) {
setActiveView(mapView);
});
on(dom.byId("scene-view-select"), "click", function (evt) {
setActiveView(sceneView);
});
on(dom.byId("area-mode"), "change", function (evt) {
clusterLayer.clusterAreaDisplay = this.value;
clusterLayer.draw();
});
function setActiveView(view) {
if (view === activeView) return; // it's already active
var viewpoint = activeView.viewpoint.clone();
var container = activeView.container;
activeView.container = null;
if (view === mapView) {
mapView.viewpoint = viewpoint;
mapView.container = container;
mapView.rotation = 0;
activeView = mapView;
}
else {
sceneView.viewpoint = viewpoint;
sceneView.container = container;
activeView = sceneView;
}
setActiveViewClasses(view);
activeView.when(() => {
//when switching views force a redraw
if (clusterLayer) {
// clusterLayer.draw(view);
}
});
}
function setActiveViewClasses(view) {
if (view === mapView) {
domClass.add(dom.byId("map-view-select"), "active");
domClass.remove(dom.byId("scene-view-select"), "active");
}
else {
domClass.add(dom.byId("scene-view-select"), "active");
domClass.remove(dom.byId("map-view-select"), "active");
}
}
});
</script>
</head>
<body>
<div id="container">
<div class="view" id="views">
</div>
<div id="top-menu">
<div class="section text">
<span id="title-text">Flare Cluster Layer - </span>API v4.12
</div>
<span class="separator"></span>
<div class="section">
<button id="map-view-select" class="button">Map View</button>
<button id="scene-view-select" class="button"> Scene View</button>
</div>
<span class="separator"></span>
<div class="section">
Area mode:
<select id="area-mode">
<option value="">None</option>
<option value="activated">Hover</option>
<option value="always">Always</option>
</select>
</div>
</div>
<div id="api-link">
<a href="index_v3.html" >Go to version 3.x</a>
</div>
</div>
</body>
</html>
首先在public/index.html引入插件
var dojoConfig = {
async: true,
tlmSiblingOfDojo: false,
packages: [{
name: "fcl",
location: 'http://192.168.31.179:8084/fcl/' // 絕對地址引入
}],
has: {
"esri-promise-compatibility": 1 // enable native promises and remove the warning about using when() instead of then(). https://developers.arcgis.com/javascript/latest/guide/release-notes/index.html#improved-compatibility-with-javascript-promises
}
};
主要的代碼,通過控制clusterRatio 範圍大小, 來控制點聚合區域, data爲點集合數據
function initLayer(data) {
const defaultSym = new SimpleMarkerSymbol({
size: 6,
color: "#FF0000",
outline: null
});
const renderer = new ClassBreaksRenderer({
defaultSymbol: defaultSym
});
renderer.field = "clusterCount";
const smSymbol = new SimpleMarkerSymbol({ size: 22, outline: new SimpleLineSymbol({ color: [221, 159, 34, 0.8] }), color: [255, 204, 102, 0.8] });
const mdSymbol = new SimpleMarkerSymbol({ size: 24, outline: new SimpleLineSymbol({ color: [82, 163, 204, 0.8] }), color: [102, 204, 255, 0.8] });
const lgSymbol = new SimpleMarkerSymbol({ size: 28, outline: new SimpleLineSymbol({ color: [41, 163, 41, 0.8] }), color: [51, 204, 51, 0.8] });
const xlSymbol = new SimpleMarkerSymbol({ size: 32, outline: new SimpleLineSymbol({ color: [200, 52, 59, 0.8] }), color: [250, 65, 74, 0.8] });
renderer.addClassBreakInfo(0, 19, smSymbol);
renderer.addClassBreakInfo(20, 150, mdSymbol);
renderer.addClassBreakInfo(151, 1000, lgSymbol);
renderer.addClassBreakInfo(1001, Infinity, xlSymbol);
let areaRenderer;
// if area display mode is set. Create a renderer to display cluster areas. Use SimpleFillSymbols as the areas are polygons
const defaultAreaSym = new SimpleFillSymbol({
style: "solid",
color: [0, 0, 0, 0.2],
outline: new SimpleLineSymbol({ color: [0, 0, 0, 0.3] })
});
areaRenderer = new ClassBreaksRenderer({
defaultSymbol: defaultAreaSym
});
areaRenderer.field = "clusterCount";
const smAreaSymbol = new SimpleFillSymbol({ color: [255, 204, 102, 0.4], outline: new SimpleLineSymbol({ color: [221, 159, 34, 0.8], style: "dash" }) });
const mdAreaSymbol = new SimpleFillSymbol({ color: [102, 204, 255, 0.4], outline: new SimpleLineSymbol({ color: [82, 163, 204, 0.8], style: "dash" }) });
const lgAreaSymbol = new SimpleFillSymbol({ color: [51, 204, 51, 0.4], outline: new SimpleLineSymbol({ color: [41, 163, 41, 0.8], style: "dash" }) });
const xlAreaSymbol = new SimpleFillSymbol({ color: [250, 65, 74, 0.4], outline: new SimpleLineSymbol({ color: [200, 52, 59, 0.8], style: "dash" }) });
areaRenderer.addClassBreakInfo(0, 19, smAreaSymbol);
areaRenderer.addClassBreakInfo(20, 150, mdAreaSymbol);
areaRenderer.addClassBreakInfo(151, 1000, lgAreaSymbol);
areaRenderer.addClassBreakInfo(1001, Infinity, xlAreaSymbol);
// Set up another class breaks renderer to style the flares individually
const flareRenderer = new ClassBreaksRenderer({
defaultSymbol: renderer.defaultSymbol
});
flareRenderer.field = "clusterCount";
const smFlareSymbol = new SimpleMarkerSymbol({ size: 14, color: [255, 204, 102, 0.8], outline: new SimpleLineSymbol({ color: [221, 159, 34, 0.8] }) });
const mdFlareSymbol = new SimpleMarkerSymbol({ size: 14, color: [102, 204, 255, 0.8], outline: new SimpleLineSymbol({ color: [82, 163, 204, 0.8] }) });
const lgFlareSymbol = new SimpleMarkerSymbol({ size: 14, color: [51, 204, 51, 0.8], outline: new SimpleLineSymbol({ color: [41, 163, 41, 0.8] }) });
const xlFlareSymbol = new SimpleMarkerSymbol({ size: 14, color: [250, 65, 74, 0.8], outline: new SimpleLineSymbol({ color: [200, 52, 59, 0.8] }) });
flareRenderer.addClassBreakInfo(0, 19, smFlareSymbol);
flareRenderer.addClassBreakInfo(20, 150, mdFlareSymbol);
flareRenderer.addClassBreakInfo(151, 1000, lgFlareSymbol);
flareRenderer.addClassBreakInfo(1001, Infinity, xlFlareSymbol);
const popupTemplate = new PopupTemplate({
title: "{deviceName}",
content: [{
type: "fields",
fieldInfos: [
{ fieldName: "deviceName", label: "deviceName", visible: true },
{ fieldName: "deviceId", label: "deviceId", visible: true },
]
}]
});
const options = {
id: "flare-cluster-layer",
clusterRenderer: renderer,
areaRenderer: areaRenderer,
flareRenderer: flareRenderer,
singlePopupTemplate: popupTemplate,
spatialReference: new SpatialReference({ "wkid": 4326 }),
subTypeFlareProperty: "deviceName", // 顯示的內容
singleFlareTooltipProperty: "deviceId", // 顯示的內容
displaySubTypeFlares: true,
maxSingleFlareCount: 10,
clusterRatio: 200, // 點聚合範圍
clusterAreaDisplay: 'activated',
data: data // 點數據
}
let clusterLayer = new fcl.FlareClusterLayer(options);
map.add(clusterLayer);
}