Skip to content

Instantly share code, notes, and snippets.

@jasonbeverage
Last active September 29, 2017 21:20
Show Gist options
  • Save jasonbeverage/5cd83bd080390ee07a8bb0932a2ace45 to your computer and use it in GitHub Desktop.
Save jasonbeverage/5cd83bd080390ee07a8bb0932a2ace45 to your computer and use it in GitHub Desktop.
Cesium Triangulation Clustering
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<meta name="description" content="Cluster labels, billboards and points.">
<meta name="cesium-sandcastle-labels" content="Tutorials,Showcases">
<title>Cesium Demo</title>
<script type="text/javascript" src="../Sandcastle-header.js"></script>
<script type="text/javascript" src="../../../ThirdParty/requirejs-2.1.20/require.js"></script>
<script type="text/javascript">
require.config({
baseUrl : '../../../Source',
waitSeconds : 60
});
</script>
</head>
<body class="sandcastle-loading" data-sandcastle-bucket="bucket-requirejs.html">
<style>
@import url(../templates/bucket.css);
#toolbar {
background: rgba(42, 42, 42, 0.8);
padding: 4px;
border-radius: 4px;
}
#toolbar input {
vertical-align: middle;
padding-top: 2px;
padding-bottom: 2px;
}
</style>
<div id="cesiumContainer" class="fullSize"></div>
<div id="loadingOverlay"><h1>Loading...</h1></div>
<div id="toolbar">
<table>
<tbody><tr>
<td>Pixel Range</td>
<td>
<input type="range" min="1" max="200" step="1" data-bind="value: pixelRange, valueUpdate: 'input'">
<input type="text" size="2" data-bind="value: pixelRange">
</td>
</tr>
<tr>
<td>Minimum Cluster Size</td>
<td>
<input type="range" min="2" max="20" step="1" data-bind="value: minimumClusterSize, valueUpdate: 'input'">
<input type="text" size="2" data-bind="value: minimumClusterSize">
</td>
</tr>
</tbody>
</table>
</div>
<script id="cesium_sandcastle_script">
function startup(Cesium) {
'use strict';
//Sandcastle_Begin
function pointInPolygon(point, positions) {
// ray-casting algorithm based on
// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
var x = point.longitude, y = point.latitude;
var inside = false;
for (var i = 0, j = positions.length - 1; i < positions.length; j = i++) {
var xi = positions[i].longitude, yi = positions[i].latitude;
var xj = positions[j].longitude, yj = positions[j].latitude;
var intersect = ((yi > y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
if (intersect) {
inside = !inside;
}
}
return inside;
}
var viewer = new Cesium.Viewer('cesiumContainer');
var options = {
camera : viewer.scene.camera,
canvas : viewer.scene.canvas
};
var polygons;
viewer.dataSources.add(Cesium.GeoJsonDataSource.load('../../SampleData/ne_10m_us_states.topojson')).then(function(ds) { polygons = ds; });
var dataSourcePromise = viewer.dataSources.add(Cesium.KmlDataSource.load('../../SampleData/kml/facilities/facilities.kml', options));
dataSourcePromise.then(function(dataSource) {
var pixelRange = 15;
var minimumClusterSize = 3;
var enabled = true;
dataSource.clustering.enabled = enabled;
dataSource.clustering.pixelRange = pixelRange;
dataSource.clustering.minimumClusterSize = minimumClusterSize;
dataSource.clustering.canCluster = function(a,b) {
if (a.billboard.image._value !== b.billboard.image._value) {
return false;
}
var entities = polygons.entities.values;
var aCarto = viewer.scene.globe.ellipsoid.cartesianToCartographic(a.position._value);
var bCarto = viewer.scene.globe.ellipsoid.cartesianToCartographic(b.position._value);
for (var i = 0; i < entities.length; i++) {
var e = entities[i];
if (!e.polygon.hierarchy._value._carto) {
e.polygon.hierarchy._value._carto = viewer.scene.globe.ellipsoid.cartesianArrayToCartographicArray(e.polygon.hierarchy._value.positions);
}
if (pointInPolygon(aCarto, e.polygon.hierarchy._value._carto) &&
pointInPolygon(bCarto, e.polygon.hierarchy._value._carto))
{
return true;
}
}
return false;
};
var removeListener;
function customStyle() {
if (Cesium.defined(removeListener)) {
removeListener();
removeListener = undefined;
} else {
removeListener = dataSource.clustering.clusterEvent.addEventListener(function(clusteredEntities, cluster) {
var image = clusteredEntities[0].billboard.image;
cluster.billboard.show = true;
cluster.billboard.id = cluster.label.id;
cluster.billboard.verticalOrigin = Cesium.VerticalOrigin.BOTTOM;
cluster.label.show = true;
cluster.billboard.image = image._value;
});
}
// force a re-cluster with the new styling
var pixelRange = dataSource.clustering.pixelRange;
dataSource.clustering.pixelRange = 0;
dataSource.clustering.pixelRange = pixelRange;
}
// start with custom style
customStyle();
var viewModel = {
pixelRange: pixelRange,
minimumClusterSize: minimumClusterSize
};
Cesium.knockout.track(viewModel);
var toolbar = document.getElementById('toolbar');
Cesium.knockout.applyBindings(viewModel, toolbar);
function subscribeParameter(name) {
Cesium.knockout.getObservable(viewModel, name).subscribe(
function(newValue) {
dataSource.clustering[name] = newValue;
}
);
}
subscribeParameter('pixelRange');
subscribeParameter('minimumClusterSize');
Sandcastle.addToggleButton('Enabled', true, function(checked) {
dataSource.clustering.enabled = checked;
});
Sandcastle.addToggleButton('Custom Styling', true, function(checked) {
customStyle();
});
var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function(movement) {
var pickedLabel = viewer.scene.pick(movement.position);
if (Cesium.defined(pickedLabel)) {
var ids = pickedLabel.id;
if (Cesium.isArray(ids)) {
for (var i = 0; i < ids.length; ++i) {
ids[i].billboard.color = Cesium.Color.RED;
}
}
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
});
//Sandcastle_End
Sandcastle.finishedLoading();
}
if (typeof Cesium !== "undefined") {
startup(Cesium);
} else if (typeof require === "function") {
require(["Cesium"], startup);
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment