Add an example that shows clustering.
ADD
examples/cluster/example.json
+{
+ "title": "Cluster points",
+ "exampleCss": ["main.css"],
+ "exampleJs": ["main.js"],
+ "about": {
+ "text": "Cluster nearby points on a map."
+ }
+}

ADD
examples/cluster/index.pug
+extends ../common/index.pug
+
+block append mainContent
+ #controls
+ .form-group(title="The data set to plot.")
+ label(for="dataset") Data Set
+ select#dataset(param-name="dataset", placeholder="Activity")
+ option(value="adderall", url="AdderallCities2015.csv", title="9555 points") Adderall
+ option(value="cities", url="cities.csv", title="30101 points") U.S. Cities
+ option(value="earthquakes", url="earthquakes.json", title="1.3 million points") Earthquakes
+ span#points-loaded
+ .form-group(title="Number of points. Leave blank for the entire original data set. If a smaller number, only a subset of points will be shown. If a larger number, some of the data will be duplicated with random offsets.")
+ label(for="points") Number of Points
+ input#points(type="number", min="1", step="100")
+ span#points-shown

ADD
examples/cluster/main.css
+#controls {
+ overflow-x: hidden;
+ overflow-y: auto;
+ position: absolute;
+ left: 10px;
+ top: 80px;
+ z-index: 1;
+ border-radius: 5px;
+ border: 1px solid grey;
+ box-shadow: 1px 1px 3px black;
+ opacity: 0.5;
+ transition: opacity 250ms ease;
+ background: #CCC;
+ color: black;
+ padding: 4px;
+ font-size: 14px;
+ max-height: calc(100% - 100px);
+ min-width: 310px;
+}
+#controls:hover {
+ opacity: 1;
+}
+#controls .form-group {
+ margin-bottom: 0;
+}
+#controls label {
+ min-width: 120px;
+}
+#controls #points {
+ width: 100px;
+}
+#controls #points-loaded,#controls #points-shown {
+ display: inline-block;
+ font-size: 11px;
+ padding-left: 5px;
+}
+#controls table input {
+ width: 55px;
+}
+#controls table th {
+ text-align: center;
+}

ADD
examples/cluster/main.js
+/* globals utils */
+
+var map = geo.map({
+ node: '#map',
+ center: {
+ x: -98,
+ y: 39
+ },
+ zoom: 3
+});
+var layer, pointFeature, points, datapoints;
+var datarows; // eslint-disable-line no-unused-vars
+
+var query = utils.getQuery();
+
+$.each(query, function (key, value) {
+ var ctlvalue, ctlkey = key;
+ switch (key) {
+ case 'dataset':
+ ctlvalue = value ? value : 'adderall';
+ break;
+ case 'points':
+ if (value.length) {
+ points = ctlvalue = parseInt(value, 10);
+ }
+ break;
+ }
+ if (ctlvalue !== undefined) {
+ $('#' + ctlkey).val(ctlvalue);
+ }
+});
+
+/**
+ * Based on the current controls, fetch a data set and show it as a clustered
+ * points.
+ */
+function fetch_data() {
+ var dataset = $('#dataset').val(),
+ url = '../../data/' + $('#dataset option:selected').attr('url');
+ $.ajax(url, {
+ success: function (resp) {
+ var rows;
+ switch (dataset) {
+ case 'adderall':
+ rows = resp.split(/\r\n|\n|\r/);
+ rows.splice(0, 1);
+ rows = rows.map(function (r) {
+ var fields = r.split(',');
+ return [fields[12], fields[24], fields[25]].map(parseFloat);
+ });
+ break;
+ case 'cities':
+ rows = resp.split(/\r\n|\n|\r/);
+ rows.splice(rows.length - 1, 1);
+ rows = rows.map(function (r) {
+ var fields = r.split('","');
+ return ['' + fields[0].replace(/(^\s+|\s+$|^"|"$)/g, '').length, fields[2].replace(/(^\s+|\s+$|^"|"$)/g, ''), fields[3].replace(/(^\s+|\s+$|^"|"$)/g, '')].map(parseFloat);
+ });
+ break;
+ case 'earthquakes':
+ rows = resp;
+ break;
+ }
+ datapoints = rows;
+ var text = 'Loaded: ' + datapoints.length;
+ $('#points-loaded').text(text).attr('title', text);
+ show_points(datapoints);
+ }
+ });
+}
+
+/* Given a set of datapoints, optionally truncate or expand it, then show it
+ * as clustered points.
+ *
+ * @param {array} datapoints an array of points to show.
+ */
+function show_points(datapoints) {
+ datarows = null;
+ var rows = datapoints;
+ var maxrows = parseInt(points, 10) || rows.length;
+ if (rows.length > maxrows) {
+ rows = rows.slice(0, maxrows);
+ } else if (rows.length < maxrows) {
+ rows = rows.slice();
+ while (rows.length < maxrows) {
+ for (var i = rows.length - 1; i >= 0 && rows.length < maxrows; i -= 1) {
+ rows.push([
+ rows[i][0] + Math.random() * 0.1 - 0.05,
+ rows[i][1] + Math.random() * 0.1 - 0.05,
+ rows[i][2] + Math.random() * 0.1 - 0.05]);
+ }
+ }
+ }
+ pointFeature.data(rows);
+ datarows = rows;
+ pointFeature.draw();
+ var text = 'Shown: ' + rows.length;
+ $('#points-shown').text(text).attr('title', text);
+}
+
+/**
+ * Handle changes to our controls.
+ *
+ * @param {object} evt jquery evt that triggered this call.
+ */
+function change_controls(evt) {
+ var ctl = $(evt.target),
+ param = ctl.attr('id'),
+ value = ctl.val();
+ if (ctl.is('[type="checkbox"]')) {
+ value = ctl.is(':checked') ? 'true' : 'false';
+ }
+ if (value === '' && ctl.attr('placeholder')) {
+ value = ctl.attr('placeholder');
+ }
+ if (!param || value === query[param]) {
+ return;
+ }
+ if (ctl.closest('table.gradient').length) {
+ param = 'gradient';
+ }
+ switch (param) {
+ case 'dataset':
+ fetch_data();
+ break;
+ case 'points':
+ points = parseInt(value);
+ show_points(datapoints);
+ break;
+ }
+ // update the url to reflect the changes
+ query[param] = value;
+ if (value === '' || (ctl.attr('placeholder') &&
+ value === ctl.attr('placeholder'))) {
+ delete query[param];
+ }
+ utils.setQuery(query);
+}
+
+function injectStyle(feature, property, clusterStyle) {
+ var styleFunc = feature.style.get(property);
+ feature.style(property, function (d) {
+ if (d.__cluster) {
+ return clusterStyle;
+ }
+ return styleFunc.apply(this, arguments);
+ });
+}
+
+map.createLayer('osm');
+layer = map.createLayer('feature', {
+ renderer: query.renderer ? (query.renderer === 'html' ? null : query.renderer) : undefined,
+ features: query.renderer ? undefined : ['point']
+});
+pointFeature = layer.createFeature('point', {})
+ .position(function (d) {
+ return {x: d[2], y: d[1]};
+ });
+injectStyle(pointFeature, 'radius', 10);
+injectStyle(pointFeature, 'stroke', true);
+injectStyle(pointFeature, 'fill', true);
+injectStyle(pointFeature, 'strokeColor', 'black');
+injectStyle(pointFeature, 'fillColor', 'black');
+injectStyle(pointFeature, 'strokeOpacity', 1);
+injectStyle(pointFeature, 'strokeWidth', 1);
+injectStyle(pointFeature, 'fillOpacity', 0.5);
+pointFeature.clustering({radius: 0.012});
+
+fetch_data();
+$('#controls').on('change', change_controls);

ADD
examples/cluster/thumb.jpg
examples/cluster/thumb.jpg
Binary files /dev/null and b/examples/cluster/thumb.jpg differ

Add an example that shows clustering.