源码分析之Leaflet中GeoJSON模块
概述
GeoJSON.js
中定义了一个GeoJSON
类,该类继承自FeatureGroup
,用于将GeoJSON数据解析并显示在地图上。此外还定义了很多静态函数和工具方法,如geometryToLayer
、coordsToLatLng
等。
源码分析
GeoJSON
类的源码实现
GeoJSON
类的源码实现如下:
export var GeoJSON = FeatureGroup.extend({initialize:function(geojson,options){Util.setOptions(this,options); //合并选项this._layers = {}; // 存储子图层的哈希表,用于快速查找if(geojson){this.addData(geojson); // 若传入geojson数据,就立即解析}},addData:function(geojson){// 统一处理数组,遍历featuresvar features = Util.isArray(geojson) ? geojson : geojson.features,i,len,feature;if(features){for(i=0,len=features.length;i<len;i++){feature=features[i];if(feature.geometries||feature.geometry||feature.features||feature.coordinates){this.addData(feature);}}return this;}var options=this.options;// 过滤不符合条件的要素if(options.filter&&!options.filter(geojson)){return this; }// 将GeoJSON几何体转换为图层var layer=geometryToLayer(geojson,options);if(!layer){return this;}// 将原始GeoJSON附加到图层的feature属性上layer.feature=asFeature(geojson);// 保留原始样式layer.defaultOptions=layer.options;// 应用options.style样式函数this.resetStyle(layer);if(options.onEachFeature){// 调用options.onEachFeature回调函数,传入要素和图层,用户可自定义逻辑options.onEachFeature(geojson,layer); }// 将图层添加到FeatureGroupreturn this.addLayer(layer);},resetStyle:function(style){return this.eachLayer(function(layer){ // 还原图层选项到初始状态this._setLayerStyle(layer,style) // 重新应用全局样式函数},this)},_setLayerStyle:function(layer,style){if(layer.setStyle){if(typeof style === 'function'){style=style(layer.feature);} layer.setStyle(style) // 调用setStyle方法设置样式}}
})
GeoJSON
类源码解析
1.initialize(geojson,options)
- 功能:初始化GeoJSON图层
- 参数:
geojson
:GeoJSON数据,支持Feature
、FeatureCollection
、Geometry
和GeometryCollection
options
:配置选项,如style
、onEachFeature
等
- 流程:
- 通过
Util.setOptions
合并默认选项与用户选项 - 初始化
_layers
对象存储子图层 - 若传入
geojson
,调用addData
方法解析数据
- 通过
2.addData(geojson)
- 功能:解析并添加 GeoJSON 数据到图层。
- 流程:
- 判断输入是否为
FeatureCollection
(检查features
属性),遍历每个Feature
。 - 对每个
Feature
应用filter
选项,过滤不需要的要素。 - 调用
geometryToLayer
将几何对象转换为 Leaflet 图层(如Polygon
,Marker
)。 - 为图层添加
feature
属性,存储原始 GeoJSON 属性。 - 调用
onEachFeature
回调,允许用户自定义图层事件(如点击弹窗)
- 判断输入是否为
3.resetStyle(style)
- 功能:重置图层样式到初始状态。
- 参数:
layer
:目标图层(若未指定,重置所有子图层)。
- 流程:
- 若未指定
layer
,遍历所有子图层递归调用自身。 - 还原图层的
options
为defaultOptions
(初始样式)。 - 调用
_setLayerStyle
应用options.style
函数。
- 若未指定
4.setStyle(style)
- 功能:动态更新所有子图层的样式。
- 参数:
style
:样式函数或对象。
- 流程:
- 遍历所有子图层,调用
_setLayerStyle
应用新样式。
- 遍历所有子图层,调用
5._setLayerStyle(layer,style)
- 内部方法:为单个图层设置样式。
- 流程:
- 若
style
为函数,执行并传入layer.feature
获取样式对象。 - 调用图层的
setStyle
方法(适用于Path
派生类如Polyline
)
- 若
静态工具函数
1.geometryToLayer(featureData,options)
- 源码:
export function geometryToLayer(geojson,options){var geometry = geojson.type === 'Feature'? geojson.geometry : geojson, // 标准化几何体coords=geometry?geometry.coordinates:null,layers=[],pointToLayer=options&&options.pointToLayer,_coordsToLatLng=options&&options.coordsToLatLng||coordsToLatLng,latlng,latlngs,i,len;if(!coords && !geometry){return null}switch(geometry.type){case 'Point':latlng=_coordsToLatLng(coords);return _pointsToLayer(pointToLayer,geojson,latlng,options); // 生成 Markercase 'MultiPoint':for(i=0,len=coords.length;i<len;i++){latlng=_coordsToLatLng(coords[i]);layers.push(_pointsToLayer(pointToLayer,geojson,latlng,options));} return new FeatureGroup(layers);case 'LineString':case 'MultiLineString':// 坐标转LatLng数组生成折线latlngs=coordsToLatLngs(coords,geometry.type === 'LineString'?0:1,_coordsToLatLng);return new Polyline(latlngs,options); case 'Polygon':case 'MultiPolygon':// 处理多边形及孔洞latlngs=coordsToLatLngs(coords,geometry.type === 'Polygon'?1:2,_coordsToLatLng);return new Polygon(latlngs,options);case 'GeometryCollection':// 处理几何集合,递归处理子几何体for(i=0,len=geometry.geometries.length;i<len;i++){var geoLayer=geometryToLayer({geometry:geometry.geometries[i],type:'Feature',properties:geojson.properties},options);if(geoLayer){layers.push(geoLayer);}}return new FeatureGroup(layers);case 'FeatureCollection':// 处理FeatureCollection,递归处理子要素for(i=0,len=geometry.feature.length;i<len;i++){var featureLayer=geometryToLayer(geometry.features[i],options);if(featureLayer){layers.push(featureLayer)}} return new FeatureGroup(layers);default:throw new Error('Invalid GeoJSON object.'); }
}
- 功能:将GeoJSON几何对象转换为图层
- 处理逻辑:
- **点(
Point
)**:调用_pointToLayer
生成Marker
。 - **线(
LineString
)**:坐标转LatLng
数组,创建Polyline
。 - **面(
Polygon
)**:坐标转多维LatLng
数组,创建Polygon
。 - **几何集合(
GeometryCollection
)**:递归处理子几何体,返回FeatureGroup
。 - **要素集合(
FeatureCollection
)**:遍历每个要素递归处理
- **点(
function _pointToLayer(pointToLayerFn,geojson,latlng,options){// 优先使用用户自定义的pointToLayer,否则创建默认Marker// 若options.markersInheritOptions=true,则使用options作为Marker的选项// 否则,使用options.markers的选项return pointToLayerFn?pointToLayerFn(geojson,latlng):new Marker(latlng,options && options.markersInheritOptions&&options);
}
2.coordsToLatLngs(coords,levelsDeep,_coordsToLatLng)
- 源码
export function coordsToLatLngs(coords,levelsDeep,_coordsToLatLng){var latlngs=[];// 递归转换坐标层级:// levelsDeep=0 → 点数组转 LatLng 数组// levelsDeep=1 → 线数组(如 MultiLineString)// levelsDeep=2 → 多边形数组(如 MultiPolygon)for(var i=0,len=coords.length,latlng;i<len;i++){latlng=levelsDeep?coordsToLatLngs(coords[i],levelsDeep-1,_coordsToLatLng):(_coordsToLatLng||coordsToLatLng)(coords[i]);latlngs.push(latlng); }return latlngs;
}
-
功能:将坐标数组转换为
LatLng
数组或多维数组 -
参数:
coords
:坐标数组levelsDeep
:转换层级,0 表示点数组,1 表示线数组,2 表示面数组_coordsToLatLng
:自定义坐标转换函数,可选
-
流程:
- 递归转换坐标层级:
levelsDeep=0
:点数组转LatLng
数组levelsDeep=1
:线数组(如MultiLineString
,[[lng,lat],[lng,lat]]
转为[LatLng,LatLng]
)
- 递归转换坐标层级:
3.coordsToLatLng
- 源码
export function coordsToLatLng(coords){return new LatLng(coords[1],coords[0],coords[2]); //注意经纬度顺序调换
}
- 功能:将 GeoJSON 坐标数组
[lng, lat, alt?]
转为LatLng
对象。
4. latLngToCoords(latlng,precision)
- 源码
export function latLngToCoords(latlng,precision){latlng=toLatLng(latlng)return latlng.alt !==undefined?[Util.formatNum(latlng.lng,precision),Util.formatNum(latlng.lat,precision),Util.formatNum(latlng.alt,precision)]:[Util.formatNum(latlng.lng,precision),Util.formatNum(latlng.lat,precision)]
}
- 功能:将
LatLng
对象转为 GeoJSON 坐标数组[lng, lat, alt?]
,支持精度控制。
5. latLngsToCoords(latlngs,levelsDeep,closed,precision)
- 源码
// 处理闭合多边形
export function latLngsToCoords(latlngs,levelsDeep,closed,precision){var coords=[];for(var i=0,len=latlngs.length;i<len;i++){coords.push(levelsDeep?latLngsToCoords(latlngs[i],LineUtil.isFlat(latlngs[i])?0:levelsDeep-1,closed,precision):latLngToCoords(latlngs[i],precision)) }// 若closed=true,将第一个点复制到末尾if(!levelsDeep && closed &&coords.length>0){coords.push(coords[0].slice()) }return coords
}
- 功能
- 处理多边形或线数组,支持多级嵌套。
- 若
closed=true
,将第一个点复制到末尾,形成闭合多边形。 - 支持精度控制。
6. getFeature(layer,newGeometry)
- 源码
export function getFeature(layer,newGeometry){return layer.feature ? Util.extend({},layer.feature,{geometry:newGeometry}):asFeature(newGeometry)
}
export function asFeature(geojson){// 统一输入为Feature对象if(geometry.type === 'Feature' || geojson.type === 'FeatureCollection'){return geojson }return {type:'Feature',properties:{},geometry: geojson}
}
- 功能:
- 从图层获取GeoJSON要素。
- 若图层已有
feature
属性,则返回合并后的要素。 - 若输入为GeoJSON对象,返回标准化的
Feature
对象。 - 若输入为
Geometry
对象,返回Feature
对象。
矢量图层的toGeoJSON
方法
var PointToGeoJSON={toGeoJSON:function(precision){return getFeature(this,{type:'Point',coordinates:latLngToCoords(this.getLatLng(),precision)})}
}
Marker.include(PointToGeoJSON);Circle.include(PointToGeoJSON);CircleMarker.include(PointToGeoJSON);Polyline.include({toGeoJSON:function(precision){var multi = !LineUtil.isFlat(this._latlngs); // 判断是否为MultiLineStringvar coords = latLngsToCoords(this._latlngs,multi?1:0,false,precision); // 转换坐标,处理精度return getFeature(this,{type:(multi?'Multi':'')+'LineString', // 动态生成对应类型coordinates:coords })}
})Polygon.include({toGeoJSON:function(precision){var holes = !LineUtil.isFlat(this._latlngs), // 检测是否有孔洞multi=holes && !LineUtil.isFlat(this._latlngs[0]); // 检测是否为MultiPolygonvar coords=latLngsToCoords(this._latlngs,multi?2:holes?1:0,true,precision); // 处理坐标层级和闭合情况if(!holes){coords=[coords]}return getFeature(this,{type:(multi?'Multi':'')+'Polygon',coordinates:coords})}
})LayerGroup.include({toMultiPoint:function(precision){var coords=[];this.eachLayer(function(layer){coords.push(layer.toGeoJSON(precision).geometry.coordinates);})return getFeature(this,{type:'MultiPoint',coordinates:coords})},toGeoJSON:function(precision){var type =this.feature && this.feature.geometry && this.feature.geometry.type;if(type === 'MultiPoint'){return this.toMultiPoint(precision); // 若为MultiPoint,直接调用toMultiPoint方法,合并所有点图层}var isGeometryCollection = type === 'GeometryCollection',jsons=[];this.eachLayer(function(layer){if(layer.toGeoJSON){var json =layer.toGeoJSON(precision);if(isGeometryCollection){jsons.push(json.geometry);}else{var feature=asFeature(json);if(feature.type === 'FeatureCollection'){jsons.push.apply(jsons,feature.features);}else{jsons.push(feature);}}}})if(isGeometryCollection){return getFeature(this,{geometries:jsons,type:'GeometryCollection'})}return {type:'FeatureCollection',features:jsons}}
})
工厂函数
export function geoJSON(geojson,options){return new GeoJSON(geojson,options);
}export var geoJson = geoJSON;
设计亮点
1. 样式隔离:通过 resetStyle
和 setStyle
实现动态样式更新,避免直接修改图层原型。
2. 递归处理:geometryToLayer
和 coordsToLatLngs
支持嵌套几何结构(如 MultiPolygon
)。
3. 数据兼容:asFeature
确保非标准 GeoJSON 输入(如纯几何体)被正确包装。
4. 坐标转换:处理 Leaflet 与 GeoJSON 的坐标顺序差异([lat, lng] vs [lng, lat]
)
总结
通过模块化设计,将 GeoJSON 解析、坐标转换、样式控制等功能解耦,支持灵活扩展(如自定义 pointToLayer
),是 Leaflet 处理地理数据的关键模块