Leaflet Google 地图和 d3 Voronoi 图叠加
Leaflet Google Map and d3 Voronoi diagram overlay
我正在关注 Chris Zetter 的 tutorial of creating a Voronoi overlay on Leaflet, but I have replaced the Mapbox tiles with Google Maps tiles (using the Google Mutant Leaflet 插件)并使用我自己的数据。
voronoi 叠加层有效,但在移动地图或重置视图(moveend 和 viewreset 事件)时不会更新。
似乎 "viewreset" 事件没有被触发,"moveend" 事件似乎没有触发 "drawWithLoading" 函数。
出现的错误是:
Uncaught TypeError: Cannot read property 'call' of undefined(…)
在指示的行上
此功能的数量:
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
})
});
这是它引用的 mapLayer 函数:
var mapLayer = {
onAdd: function(map) {
map.on('viewreset moveend', drawWithLoading);
drawWithLoading();
},
};
这里是 drawWithLoading 函数:
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);
}
我对 javascript 很陌生,过去几天一直在努力解决这个问题。
有谁知道为什么会出现 mapLayer 函数错误,以及为什么地图不会在 "moveend" 和 "viewreset" 事件上重绘?
编辑为包含完整代码:
// 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);
即使您发布了一些代码和 fiddle,也很难知道发生了什么。请尝试更新您的问题以提供 complete example.
我要大胆猜测并指向这一行代码:
var mapLayer = {
...
};
如果你读过 Leaflet tutorials,特别是关于扩展 Leaflet 以创建新类型的地图层的那些,你会意识到创建 [=12= 的子class 的方法] 是:
var mapLayer = L.Layer.extend({
onAdd: function(map) {...},
...
});
Leaflet 期望地图图层是 L.Layer
的实例,实现所有内部方法。如果您尝试添加未实现 L.Layer
"interface" 的内容,事情将会中断。
如果您不了解 subclassing 和 class 接口等,请刷新您对 OOP.
的了解
这可能不是您的代码中的唯一问题,但这是我现在可以看到的唯一问题,而没有仔细查看完整的示例。
我正在关注 Chris Zetter 的 tutorial of creating a Voronoi overlay on Leaflet, but I have replaced the Mapbox tiles with Google Maps tiles (using the Google Mutant Leaflet 插件)并使用我自己的数据。
voronoi 叠加层有效,但在移动地图或重置视图(moveend 和 viewreset 事件)时不会更新。
似乎 "viewreset" 事件没有被触发,"moveend" 事件似乎没有触发 "drawWithLoading" 函数。
出现的错误是:
Uncaught TypeError: Cannot read property 'call' of undefined(…)
在指示的行上
此功能的数量:
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
})
});
这是它引用的 mapLayer 函数:
var mapLayer = {
onAdd: function(map) {
map.on('viewreset moveend', drawWithLoading);
drawWithLoading();
},
};
这里是 drawWithLoading 函数:
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);
}
我对 javascript 很陌生,过去几天一直在努力解决这个问题。
有谁知道为什么会出现 mapLayer 函数错误,以及为什么地图不会在 "moveend" 和 "viewreset" 事件上重绘?
编辑为包含完整代码:
// 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);
即使您发布了一些代码和 fiddle,也很难知道发生了什么。请尝试更新您的问题以提供 complete example.
我要大胆猜测并指向这一行代码:
var mapLayer = {
...
};
如果你读过 Leaflet tutorials,特别是关于扩展 Leaflet 以创建新类型的地图层的那些,你会意识到创建 [=12= 的子class 的方法] 是:
var mapLayer = L.Layer.extend({
onAdd: function(map) {...},
...
});
Leaflet 期望地图图层是 L.Layer
的实例,实现所有内部方法。如果您尝试添加未实现 L.Layer
"interface" 的内容,事情将会中断。
如果您不了解 subclassing 和 class 接口等,请刷新您对 OOP.
的了解这可能不是您的代码中的唯一问题,但这是我现在可以看到的唯一问题,而没有仔细查看完整的示例。