// 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; }