// This is a Chiasm component that implements a bubble map.
// Based on chiasm-leaflet.
function BubbleMap() {
// Extend chiasm-leaflet using composition (not inheritence).
var my = ChiasmLeaflet();
// my.map is the Leaflet instance.
// TODO move this into chiasm-component.
my.addPublicProperties = function (publicProperties){
Object.keys(publicProperties).forEach(function (property){
my.addPublicProperty(property, publicProperties[property]);
});
};
my.addPublicProperties({
// This is the data column that maps to bubble size.
// "r" stands for radius.
rColumn: Model.None,
// The circle radius used if rColumn is not specified.
rDefault: 3,
// The range of the radius scale if rColumn is specified.
rMin: 0,
rMax: 10,
fillColor: "black"
});
var rScale = d3.scale.sqrt();
// Add a semi-transparent white layer to fade the
// black & white base map to the background.
var canvasTiles = L.tileLayer.canvas();
canvasTiles.drawTile = function(canvas, tilePoint, zoom) {
var ctx = canvas.getContext('2d');
// TODO move opacity to config.
ctx.fillStyle = "rgba(255, 255, 250, 0.85)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
canvasTiles.addTo(my.map);
// Generate a function or constant for circle radius,
// depending on whether or not rColumn is defined.
my.when(["data", "rColumn", "rDefault", "rMin", "rMax"],
function (data, rColumn, rDefault, rMin, rMax){
if(rColumn === Model.None){
my.r = function (){ return rDefault};
} else {
rScale
.domain([0, d3.max(data, function (d){ return d[rColumn]; })])
.range([rMin, rMax]);
my.r = function (d){ return rScale(d[rColumn]); };
}
});
var oldMarkers = [];
my.when(["data", "r", "fillColor"], _.throttle(function (data, r, fillColor){
oldMarkers.forEach(function (marker){
my.map.removeLayer(marker);
});
// TODO move these to config.
var latitudeColumn = "latitude";
var longitudeColumn = "longitude";
oldMarkers = data.map(function (d){
var lat = d[latitudeColumn];
var lng = d[longitudeColumn];
var markerCenter = L.latLng(lat, lng);
var circleMarker = L.circleMarker(markerCenter, {
color: fillColor,
weight: 1,
// TODO move this to config.
fillOpacity: 1,
opacity: 0,
weight: 0,
clickable: true
});
circleMarker.setRadius(r(d));
circleMarker.addTo(my.map)
.bindPopup([
d.name + " - " + d.deaths + " death" + (d.deaths > 1 ? "s" : ""),
d.note,
"source: " + "" + d.source_name + ""
].join("
"));
//if(d.name === "Le Petit Cambodge"){
// circleMarker.openPopup();
//}
return circleMarker;
});
}, 100));
return my;
}