这是一篇快速示范,使用D3结合Leaflet来生成GeoJSON图形。实现简单明确,但是遗憾的是,Leaflet缺少自定义覆盖图层的文档和示例,所以希望能帮助入门。(注:Leaflet文档已经更新,在ILayer章节中有《Implementing Custom Layers》(实现自定义图层))。
#初始化Map和SVG覆盖层
创建基于MapBox瓦片的基本地图
var map = new L.Map("map", {center: [37.8, -96.9], zoom: 4}) .addLayer(new L.TileLayer("http://{s}.tiles.mapbox.com/v3/examples.map-vyofok3q/{z}/{x}/{y}.png"));
地图放置于id为“map”的页面元素中,指定的样式尺寸为:
#map { width: 960px; height: 500px; }
接下来,向Leaflet的overlay图层添加SVG元素。当地图漫游时,Leaflet会自动重新定位overlay框。需要注意的时,SVG元素初始化时没有宽度width和高度height,尺寸必须动态设置,因为随着地图的缩放会改变。
var svg = d3.select(map.getPanes().overlayPane).append("svg"), g = svg.append("g").attr("class", "leaflet-zoom-hide");
在SVG中,还需要一个G(group)元素,用来转换SVG元素,确保SVG左上角坐标(0,0),与Leaflet的图层原点相一致。“leaflet-zoom-hide”类用来在leaflet缩放时隐藏覆盖图层overlay。另外,在构造map时可以使用zoomAnimation选项来禁止过渡效果。
#加载和投影GeoJSON
使用d3.json来加载GeoJSON文件:
d3.json("us-states.json", function(collection) { // code here });
加载是异步进行,所以本实例剩余代码均位于回调函数中。传递来的collection是json文件的内容,单个的FeatureCollection,包含50个州以及波多黎各的坐标特征。
D3和Leaflet使用不同的API来生成图形,投影点。幸运的是,很容易应用自定义几何转换来让Leaflet的API适应D3。transform将一个输入的几何形状(例如用球形几何坐标表示的多边形),转换输出为不同的几何形状(例如用投影屏幕坐标表示的多边形)。利用d3.geo.transform可以作为一个简单方法来投影单个点。
function projectPoint(x, y) { var point = map.latLngToLayerPoint(new L.LatLng(y, x)); this.stream.point(point.x, point.y); }
创建d3.geo.path来投影GeoJSON
var transform = d3.geo.transform({point: projectPoint}), path = d3.geo.path().projection(transform);
利用D3的data方法为每个特征创建path元素
var feature = g.selectAll("path") .data(collection.features) .enter().append("path");
path元素初始化时为空,稍后设置其d属性来赋予path数据。
feature.attr("d", path);
selection.attr方法为每个特征赋予path数据。path元素绑定到相关的GeoJSON特征,这些特征被送至path生成器(d3.geo.path),调用自定义投影方法,依次调用Leaflet的投影方法。
# SVG适应图层
SVG元素需要多大?不能简单地设定为960*500,因为用户可以缩放和漫游地图,所以SVG大小应当取决于显示的几何特征和当前缩放级别。
根据任意的投影是很难计算SVG的盒大小。D3提供了便利机制(path.bounds方法)来计算投影后的盒大小,利用自定义transform方法将经纬度坐标转换为像素。
var bounds = path.bounds(collection), topLeft = bounds[0], bottomRight = bounds[1];
为了在图层原点上方或左侧显示几何特征,需要给SVG尺寸设置足够的填充空间。图层原点是任意的,有时需要在其上方或左侧绘制,不需要额外的填充空间。左上角会被遮挡住,一些特征将会隐藏。需要注意的是,设置left和top样式,需要依赖SVG元素的位置属性为relative。
svg .attr("width", bottomRight[0] - topLeft[0]) .attr("height", bottomRight[1] - topLeft[1]) .style("left", topLeft[0] + "px") .style("top", topLeft[1] + "px"); g .attr("transform", "translate(" + -topLeft[0] + "," + -topLeft[1] + ")");
最后,上述代码需要被Leaflet的viewreset事件所响应,确保当地图缩放时,SVG可以重新定位和重绘。查看完整的源代码。
小结,D3+Leaflet绘制SVG过程:
1.Geo投影,D3自带,也可自定义。