I am following Chris Zetter's tutorial of creating a Voronoi overlay on Leaflet, but I have replaced the Mapbox tiles with Google Maps tiles (using the Google Mutant Leaflet plugin) and used my own data.
The voronoi overlay works, but does not update when the map is moved or the view is reset (moveend and viewreset events).
It seems that the "viewreset" event doesn't get fired, and the "moveend" event doesn't trigger the "drawWithLoading" function.
The error that comes up is:
Uncaught TypeError: Cannot read property 'call' of undefined(…)
which is on the indicated line
of this function:
map.on('load', function() {
d3.csv(url, function(csv) {
points = csv;
points.forEach(function(point) {
pointTypes.set(point.type, {type: point.type, color: point.color});
})
drawPointTypeSelection();
map.addLayer(mapLayer); //ERROR on this line
})
});
Here is the mapLayer function it is refering to:
var mapLayer = {
onAdd: function(map) {
map.on('viewreset moveend', drawWithLoading);
drawWithLoading();
},
};
And here is the drawWithLoading function:
var drawWithLoading = function(e){
console.log('drawWithLoading e', e); //TODO: delete later
d3.select('#loading').classed('visible', true);
if (e && e.type == 'viewreset') {
d3.select('#overlay').remove();
}
setTimeout(function(){
draw();
d3.select('#loading').classed('visible', false);
}, 0);
}
I'm quite new with javascript and have been trying to figure this out for the past few days.
Does anyone know why the mapLayer function error comes up, and why the map doesn't redraw on the "moveend" and "viewreset" events?
Edited to include entire code:
// Selects check boxes in selector menu //
showHide = function(selector) {
d3.select(selector).select('.hide').on('click', function(){
d3.select(selector)
.classed('visible', false)
.classed('hidden', true);
});
d3.select(selector).select('.show').on('click', function(){
d3.select(selector)
.classed('visible', true)
.classed('hidden', false);
});
}
// Draws Voronoi Map //
voronoiMap = function(map, url, initialSelections) {
console.log('We got in here!');
console.log('map', map);
console.log('url', url);
console.log('initialSelections', initialSelections);
window.myMap = map;
var pointTypes = d3.map(),
points = [],
lastSelectedPoint;
var voronoi = d3.geom.voronoi()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; });
var selectPoint = function() {
d3.selectAll('.selected').classed('selected', false);
var cell = d3.select(this),
point = cell.datum();
lastSelectedPoint = point;
cell.classed('selected', true);
d3.select('#selected h1')
.html('')
.append('a')
.text(point.name)
.attr('href', point.url)
.attr('target', '_blank')
}
var drawPointTypeSelection = function() {
showHide('#selections')
labels = d3.select('#toggles').selectAll('input')
.data(pointTypes.values())
.enter().append("label");
labels.append("input")
.attr('type', 'checkbox')
.property('checked', function(d) {
return initialSelections === undefined || initialSelections.has(d.type)
})
.attr("value", function(d) { return d.type; })
.on("change", drawWithLoading);
labels.append("span")
.attr('class', 'key')
.style('background-color', function(d) { return '#' + d.color; });
labels.append("span")
.text(function(d) { return d.type; });
}
var selectedTypes = function() {
return d3.selectAll('#toggles input[type=checkbox]')[0].filter(function(elem) {
return elem.checked;
}).map(function(elem) {
return elem.value;
})
}
var pointsFilteredToSelectedTypes = function() {
var currentSelectedTypes = d3.set(selectedTypes());
return points.filter(function(item){
return currentSelectedTypes.has(item.type);
});
}
var drawWithLoading = function(e){
console.log('drawWithLoading e', e); //TODO: delete later
d3.select('#loading').classed('visible', true);
if (e && e.type == 'viewreset') {
d3.select('#overlay').remove();
}
setTimeout(function(){
draw();
d3.select('#loading').classed('visible', false);
}, 0);
}
var draw = function() {
d3.select('#overlay').remove();
var bounds = map.getBounds(),
topLeft = map.latLngToLayerPoint(bounds.getNorthWest()),
bottomRight = map.latLngToLayerPoint(bounds.getSouthEast()),
existing = d3.set(),
drawLimit = bounds.pad(0.4);
filteredPoints = pointsFilteredToSelectedTypes().filter(function(d) {
var latlng = new L.LatLng(d.latitude, d.longitude);
if (!drawLimit.contains(latlng)) { return false };
var point = map.latLngToLayerPoint(latlng);
key = point.toString();
if (existing.has(key)) { return false };
existing.add(key);
d.x = point.x;
d.y = point.y;
return true;
});
voronoi(filteredPoints).forEach(function(d) { d.point.cell = d; });
console.log('map.getPanes()', map.getPanes());
var svg = d3.select(map.getPanes().overlayPane).append("svg")
.attr('id', 'overlay')
.attr("class", "leaflet-zoom-hide")
.style("width", map.getSize().x + 'px')
.style("height", map.getSize().y + 'px')
.style("margin-left", topLeft.x + "px")
.style("margin-top", topLeft.y + "px");
var g = svg.append("g")
.attr("transform", "translate(" + (-topLeft.x) + "," + (-topLeft.y) + ")");
var svgPoints = g.attr("class", "points")
.selectAll("g")
.data(filteredPoints)
.enter().append("g")
.attr("class", "point")
.attr('data-name', function(d) { return d.name } );
var buildPathFromPoint = function(point) {
return "M" + point.cell.join("L") + "Z";
}
svgPoints.append("path")
.attr("class", "point-cell")
.attr("d", buildPathFromPoint)
.style('fill', function(d) { //return '#' + d.color
if (d.name <= 10) {return "ffffd9"}
else if (d.name <=15 && d.name >10) {return "edf8b1"}
else if (d.name <=20 && d.name >15) {return "7fcdbb"}
else if (d.name <=25 && d.name >20) {return "41b6c4"}
else if (d.name <=35 && d.name >25) {return "1d91c0"}
else if (d.name <=45 && d.name >35) {return "253494"}
else {return "081d58"}
})
.attr("opacity", 0.5)
.on('click', selectPoint)
.classed("selected", function(d) { return lastSelectedPoint == d} );
svgPoints.append("circle")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.style('fill', function(d) { //return '#' + d.color
if (d.name <= 10) {return "ffffd9"}
else if (d.name <=15 && d.name >10) {return "edf8b1"}
else if (d.name <=20 && d.name >15) {return "7fcdbb"}
else if (d.name <=25 && d.name >20) {return "41b6c4"}
else if (d.name <=35 && d.name >25) {return "1d91c0"}
else if (d.name <=45 && d.name >35) {return "253494"}
else {return "081d58"} } )
.attr("r", 1.4)
.attr("opacity", 0.6);
}
var mapLayer = {
onAdd: function(map) {
console.log("onviewreset map", map); //TODO: Delete later
map.on('viewreset moveend', drawWithLoading);
drawWithLoading();
},
};
showHide('#about');
map.on('load', function() {
console.log('map ready');
d3.csv(url, function(csv) {
points = csv;
points.forEach(function(point) {
pointTypes.set(point.type, {type: point.type, color: point.color});
})
drawPointTypeSelection();
console.log("addLayer"); //TODO: Delete Later
map.addLayer(mapLayer);
console.log("addedLayer"); //TODO: Delete Later
})
});
console.log('got here too');
}
// Draws Google Maps tiles using Road and Style tiles //
var map = L.map('map');
var roadMutant = L.gridLayer.googleMutant({
maxZoom: 24,
type:'roadmap'
}).addTo(map);
var styleMutant = L.gridLayer.googleMutant({
styles: [
{elementType: 'labels', stylers: [{visibility: 'off'}]},
{featureType: 'water', stylers: [{color: '#444444'}]},
{featureType: 'landscape', stylers: [{color: '#eeeeee'}]},
{featureType: 'road', stylers: [{visibility: 'on'}]},
{featureType: 'poi', stylers: [{visibility: 'off'}]},
{featureType: 'transit', stylers: [{visibility: 'on'}]},
{featureType: 'administrative', stylers: [{visibility: 'off'}]},
{featureType: 'administrative.locality', stylers: [{visibility: 'off'}]}
],
maxZoom: 24,
type:'roadmap'
});
// Creates a styles selector menu //
L.control.layers({
Roadmap: roadMutant,
Styles: styleMutant
}, {}, {
collapsed: false
}).addTo(map);
url = 'AllCoords_supertrial.csv';
initialSelection = d3.set(['1','2','3']); //Ideally would like to have all selected to start with or take away having to check boxes before drawing//
voronoiMap(map, url, initialSelection);